diff --git a/Demo/Windows/PixelFarmSample.WinForms/Form1.cs b/Demo/Windows/PixelFarmSample.WinForms/Form1.cs index 5fb05621..597abcec 100644 --- a/Demo/Windows/PixelFarmSample.WinForms/Form1.cs +++ b/Demo/Windows/PixelFarmSample.WinForms/Form1.cs @@ -11,7 +11,6 @@ using PixelFarm.Contours; using Typography.OpenFont; -using Typography.OpenFont.Trimmable; using Typography.TextLayout; using Typography.Contours; using Typography.WebFont; @@ -817,8 +816,8 @@ private void button3_Click(object sender, EventArgs e) OpenFontReader openFontReader = new OpenFontReader(); PreviewFontInfo previewFontInfo = openFontReader.ReadPreview(fs); } - } - + } + private void cmdTestReloadGlyphs_Click(object sender, EventArgs e) { diff --git a/Demo/Windows/PixelFarmSample.WinForms/Program.cs b/Demo/Windows/PixelFarmSample.WinForms/Program.cs index e053181c..c9de622a 100644 --- a/Demo/Windows/PixelFarmSample.WinForms/Program.cs +++ b/Demo/Windows/PixelFarmSample.WinForms/Program.cs @@ -1,6 +1,8 @@ -//MIT, 2016-2017, WinterDev +//MIT, 2016-present, WinterDev using System; using System.Windows.Forms; +using Typography.OpenFont; + namespace SampleWinForms { static class Program @@ -11,10 +13,40 @@ static class Program [STAThread] static void Main() { + //--------- + //user can skip this, and set it by app-manifest + //but here I, set DPI-aware by API :) + //------- + + + bool dpi_result = SetProcessDPIAware(); + Typeface.DefaultDpi = GetDpiForSystem(); + + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } + + + //------- + //https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware?redirectedfrom=MSDN + [System.Runtime.InteropServices.DllImport("user32")] + static extern bool SetProcessDPIAware(); + + //UINT GetDpiForWindow( + // HWND hwnd + //); + //https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow + + [System.Runtime.InteropServices.DllImport("user32")] + static extern uint GetDpiForWindow(IntPtr ptr); + + //UINT GetDpiForSystem(); + //https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforsystem + [System.Runtime.InteropServices.DllImport("user32")] + static extern uint GetDpiForSystem(); + //------- } } diff --git a/Demo/Windows/PixelFarmTextBox.WinForms/SampleTextBox.cs b/Demo/Windows/PixelFarmTextBox.WinForms/SampleTextBox.cs index b0e0e534..bc4cb2c7 100644 --- a/Demo/Windows/PixelFarmTextBox.WinForms/SampleTextBox.cs +++ b/Demo/Windows/PixelFarmTextBox.WinForms/SampleTextBox.cs @@ -1,4 +1,4 @@ -//MIT, 2016-2017, WinterDev +//MIT, 2016-present, WinterDev using System; using System.Windows.Forms; namespace SampleWinForms diff --git a/PixelFarm.Typography/2_CpuBlit_BitmapAtlas/GlyphTextureBitmapGenerator.cs b/PixelFarm.Typography/2_CpuBlit_BitmapAtlas/GlyphTextureBitmapGenerator.cs index 5d929189..90accea9 100644 --- a/PixelFarm.Typography/2_CpuBlit_BitmapAtlas/GlyphTextureBitmapGenerator.cs +++ b/PixelFarm.Typography/2_CpuBlit_BitmapAtlas/GlyphTextureBitmapGenerator.cs @@ -36,15 +36,37 @@ public void SetSvgBmpBuilderFunc(PixelFarm.Drawing.SvgBmpBuilderFunc svgBmpBuild _svgBmpBuilderFunc = svgBmpBuilderFunc; } - + Typeface _typeface; + float _sizeInPoints; + float _px_scale; OnEachGlyph _onEachGlyphDel; + int _currentDPI; + + void SetCurrentFontInfo(Typeface typeface, float sizeInPoints) + { + + _typeface = typeface; + _sizeInPoints = sizeInPoints; + + //please note that DPI effect glyph size + //TODO: + //1. add information about dpi to generated texture + //2. when dpi is changed we need to use correct texture for that dpi + + + _currentDPI = (int)Typeface.DefaultDpi; + _px_scale = _typeface.CalculateScaleToPixelFromPointSize(_sizeInPoints, _currentDPI); + } public SimpleBitmapAtlasBuilder CreateTextureFontFromBuildDetail( - Typeface typeface, float sizeInPoint, + Typeface typeface, + float sizeInPoint, TextureKind textureKind, GlyphTextureBuildDetail[] details, OnEachGlyph onEachGlyphDel = null) { + _onEachGlyphDel = onEachGlyphDel; + SetCurrentFontInfo(typeface, sizeInPoint); //------------------------------------------------------------- var atlasBuilder = new SimpleBitmapAtlasBuilder(); @@ -66,8 +88,7 @@ public SimpleBitmapAtlasBuilder CreateTextureFontFromBuildDetail( { glyphIndexList[m] = m; } - CreateTextureFontFromGlyphIndices(typeface, - sizeInPoint, + CreateTextureFontFromGlyphIndices( detail.HintTechnique, atlasBuilder, glyphIndexList @@ -79,8 +100,7 @@ public SimpleBitmapAtlasBuilder CreateTextureFontFromBuildDetail( //2. find associated glyph index base on input script langs List outputGlyphIndexList = new List(); typeface.CollectAllAssociateGlyphIndex(outputGlyphIndexList, detail.ScriptLang); - CreateTextureFontFromGlyphIndices(typeface, - sizeInPoint, + CreateTextureFontFromGlyphIndices( detail.HintTechnique, atlasBuilder, GetUniqueGlyphIndexList(outputGlyphIndexList) @@ -95,8 +115,7 @@ public SimpleBitmapAtlasBuilder CreateTextureFontFromBuildDetail( //skip those script lang=null //2. find associated glyph index base on input script langs - CreateTextureFontFromGlyphIndices(typeface, - sizeInPoint, + CreateTextureFontFromGlyphIndices( detail.HintTechnique, atlasBuilder, detail.OnlySelectedGlyphIndices @@ -116,6 +135,7 @@ public SimpleBitmapAtlasBuilder CreateTextureFontFromInputChars( { _onEachGlyphDel = onEachGlyphDel; + SetCurrentFontInfo(typeface, sizeInPoint); //convert input chars into glyphIndex List glyphIndices = new List(chars.Length); int i = 0; @@ -129,15 +149,16 @@ public SimpleBitmapAtlasBuilder CreateTextureFontFromInputChars( atlasBuilder.SetAtlasInfo(textureKind, sizeInPoint); //------------------------------------------------------------- //we can specfic subset with special setting for each set - CreateTextureFontFromGlyphIndices(typeface, sizeInPoint, - HintTechnique.TrueTypeInstruction_VerticalOnly, atlasBuilder, GetUniqueGlyphIndexList(glyphIndices)); + CreateTextureFontFromGlyphIndices( + HintTechnique.TrueTypeInstruction_VerticalOnly, + atlasBuilder, + GetUniqueGlyphIndexList(glyphIndices)); _onEachGlyphDel = null;//reset return atlasBuilder; } + void CreateTextureFontFromGlyphIndices( - Typeface typeface, - float sizeInPoint, HintTechnique hintTechnique, SimpleBitmapAtlasBuilder atlasBuilder, char[] chars) @@ -146,17 +167,17 @@ void CreateTextureFontFromGlyphIndices( ushort[] glyphIndices = new ushort[j]; for (int i = 0; i < j; ++i) { - glyphIndices[i] = typeface.GetGlyphIndex(chars[i]); + glyphIndices[i] = _typeface.GetGlyphIndex(chars[i]); } - CreateTextureFontFromGlyphIndices(typeface, sizeInPoint, hintTechnique, atlasBuilder, glyphIndices); + CreateTextureFontFromGlyphIndices(hintTechnique, atlasBuilder, glyphIndices); } - GlyphBitmap GetGlyphBitmapFromColorOutlineGlyph(Typeface typeface, ushort glyphIndex, GlyphMeshStore glyphMeshStore, ushort colorLayerStart) + GlyphBitmap GetGlyphBitmapFromColorOutlineGlyph(ushort glyphIndex, GlyphMeshStore glyphMeshStore, ushort colorLayerStart) { //not found=> create a newone - Typography.OpenFont.Tables.COLR _colrTable = typeface.COLRTable; - Typography.OpenFont.Tables.CPAL _cpalTable = typeface.CPALTable; + Typography.OpenFont.Tables.COLR _colrTable = _typeface.COLRTable; + Typography.OpenFont.Tables.CPAL _cpalTable = _typeface.CPALTable; Q1RectD totalBounds = Q1RectD.ZeroIntersection(); @@ -213,25 +234,24 @@ GlyphBitmap GetGlyphBitmapFromColorOutlineGlyph(Typeface typeface, ushort glyphI ImageStartY = -offset_y //offset back }; } - GlyphBitmap GetGlyphBitmapFromBitmapFont(Typeface typeface, float sizeInPoint, ushort glyphIndex) + GlyphBitmap GetGlyphBitmapFromBitmapFont(ushort glyphIndex) { //not found=> create a new one - if (typeface.IsBitmapFont) + if (_typeface.IsBitmapFont) { //try load using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { //load actual bitmap font - Glyph glyph = typeface.GetGlyph(glyphIndex); - typeface.ReadBitmapContent(glyph, ms); + Glyph glyph = _typeface.GetGlyph(glyphIndex); + _typeface.ReadBitmapContent(glyph, ms); using (MemBitmap memBitmap = MemBitmapExt.LoadBitmap(ms)) { //bitmap that are load may be larger than we need //so we need to scale it to specfic size - float bmpScale = typeface.CalculateScaleToPixelFromPointSize(sizeInPoint); - float target_advW = typeface.GetAdvanceWidthFromGlyphIndex(glyphIndex) * bmpScale; + float target_advW = _typeface.GetAdvanceWidthFromGlyphIndex(glyphIndex) * _px_scale; float scaleForBmp = target_advW / memBitmap.Width; MemBitmap scaledMemBmp = memBitmap.ScaleImage(scaleForBmp, scaleForBmp); @@ -249,16 +269,15 @@ GlyphBitmap GetGlyphBitmapFromBitmapFont(Typeface typeface, float sizeInPoint, u return null; } - GlyphBitmap GetGlyphBitmapFromSvg(Typeface typeface, float sizeInPoint, ushort glyphIndex) + GlyphBitmap GetGlyphBitmapFromSvg(ushort glyphIndex) { //TODO: use string builder from pool? var stbuilder = new System.Text.StringBuilder(); - typeface.ReadSvgContent(glyphIndex, stbuilder); + _typeface.ReadSvgContent(glyphIndex, stbuilder); - float bmpScale = typeface.CalculateScaleToPixelFromPointSize(sizeInPoint); - float target_advW = typeface.GetAdvanceWidthFromGlyphIndex(glyphIndex) * bmpScale; + float target_advW = _typeface.GetAdvanceWidthFromGlyphIndex(glyphIndex) * _px_scale; var req = new PixelFarm.Drawing.SvgBmpBuilderReq { @@ -290,9 +309,46 @@ GlyphBitmap GetGlyphBitmapFromSvg(Typeface typeface, float sizeInPoint, ushort g ImageStartY = -offset_y //offset back }; } + + + + readonly struct GlyphNotFoundHelper + { + readonly SimpleBitmapAtlasBuilder _atlasBuilder; + readonly GlyphOutlineBuilder _outlineBuilder; + readonly OnEachGlyph _onEachGlyphDel; + readonly AggGlyphTextureGen _aggTextureGen; + readonly float _sizeInPoints; + public GlyphNotFoundHelper( + SimpleBitmapAtlasBuilder atlasBuilder, + GlyphOutlineBuilder outlineBuilder, + OnEachGlyph onEachGlyphDel, + AggGlyphTextureGen aggTextureGen, float sizeInPoints) + { + _atlasBuilder = atlasBuilder; + _outlineBuilder = outlineBuilder; + _onEachGlyphDel = onEachGlyphDel; + _aggTextureGen = aggTextureGen; + _sizeInPoints = sizeInPoints; + } + public void HandleNotFoundGlyph(ushort glyphIndex) + { + +#if DEBUG + System.Diagnostics.Debug.WriteLine("bitmap texture,create fallback glyph:" + glyphIndex); +#endif + + //NESTED + //draw a glyph into tmpMemBmp and then copy to a GlyphImage + //build glyph + _outlineBuilder.BuildFromGlyphIndex(glyphIndex, _sizeInPoints); + BitmapAtlasItemSource glyphImg = _aggTextureGen.CreateAtlasItem(_outlineBuilder, 1); + glyphImg.UniqueInt16Name = glyphIndex; + _onEachGlyphDel?.Invoke(glyphImg); + _atlasBuilder.AddItemSource(glyphImg); + } + } void CreateTextureFontFromGlyphIndices( - Typeface typeface, - float sizeInPoint, HintTechnique hintTechnique, SimpleBitmapAtlasBuilder atlasBuilder, ushort[] glyphIndices) @@ -300,21 +356,31 @@ void CreateTextureFontFromGlyphIndices( //sample: create sample msdf texture //------------------------------------------------------------- - var outlineBuilder = new GlyphOutlineBuilder(typeface); + var outlineBuilder = new GlyphOutlineBuilder(_typeface); outlineBuilder.SetHintTechnique(hintTechnique); // AggGlyphTextureGen aggTextureGen = new AggGlyphTextureGen(); + GlyphNotFoundHelper glyphNotFoundHelper = new GlyphNotFoundHelper(atlasBuilder, + outlineBuilder, + _onEachGlyphDel, + aggTextureGen, + _sizeInPoints); + + //create reusable agg painter*** //assume each glyph size= 2 * line height //TODO: review here again... - int tmpMemBmpHeight = (int)(2 * typeface.CalculateRecommendLineSpacing() * typeface.CalculateScaleToPixelFromPointSize(sizeInPoint)); + + //please note that DPI effect glyph size //*** + + int tmpMemBmpHeight = (int)(2 * _typeface.CalculateRecommendLineSpacing() * _px_scale); // if (atlasBuilder.TextureKind == TextureKind.Msdf) { - float pxscale = typeface.CalculateScaleToPixelFromPointSize(sizeInPoint); + var msdfGenParams = new Msdfgen.MsdfGenParams(); int j = glyphIndices.Length; @@ -333,7 +399,7 @@ void CreateTextureFontFromGlyphIndices( using (Tools.BorrowVxs(out var vxs)) { - glyphToVxs.WriteUnFlattenOutput(vxs, pxscale); + glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); @@ -359,7 +425,7 @@ void CreateTextureFontFromGlyphIndices( using (Tools.BorrowVxs(out var vxs)) { - glyphToVxs.WriteUnFlattenOutput(vxs, pxscale); + glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); @@ -376,7 +442,7 @@ void CreateTextureFontFromGlyphIndices( int j = glyphIndices.Length; GlyphMeshStore glyphMeshStore = new GlyphMeshStore(); - glyphMeshStore.SetFont(typeface, sizeInPoint); + glyphMeshStore.SetFont(_typeface, _sizeInPoints); aggTextureGen.TextureKind = TextureKind.Bitmap; using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square @@ -387,46 +453,30 @@ void CreateTextureFontFromGlyphIndices( #endif - void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) - { - -#if DEBUG - System.Diagnostics.Debug.WriteLine("bitmap texture,create fallback glyph:" + glyphIndex); -#endif - - //NESTED - //draw a glyph into tmpMemBmp and then copy to a GlyphImage - //build glyph - outlineBuilder.BuildFromGlyphIndex(glyphIndex, sizeInPoint); - BitmapAtlasItemSource glyphImg = aggTextureGen.CreateAtlasItem(outlineBuilder, 1); - glyphImg.UniqueInt16Name = glyphIndex; - onEachDel?.Invoke(glyphImg); - atlasBuilder.AddItemSource(glyphImg); - } - - - if (typeface.HasColorTable()) + if (_typeface.HasColorTable()) { //outline glyph for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; - if (!typeface.COLRTable.LayerIndices.TryGetValue(gindex, out ushort colorLayerStart)) + if (!_typeface.COLRTable.LayerIndices.TryGetValue(gindex, out ushort colorLayerStart)) { //not found, then render as normal //TODO: impl //create glyph img - HandleNotFoundGlyph(gindex, _onEachGlyphDel); + glyphNotFoundHelper.HandleNotFoundGlyph(gindex); + } else { //TODO: review this again - GlyphBitmap glyphBmp = GetGlyphBitmapFromColorOutlineGlyph(typeface, gindex, glyphMeshStore, colorLayerStart); + GlyphBitmap glyphBmp = GetGlyphBitmapFromColorOutlineGlyph(gindex, glyphMeshStore, colorLayerStart); if (glyphBmp == null) { - HandleNotFoundGlyph(gindex, _onEachGlyphDel); + + glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { @@ -454,7 +504,7 @@ void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) } - else if (typeface.IsBitmapFont) + else if (_typeface.IsBitmapFont) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with Noto Color Emoji @@ -462,10 +512,11 @@ void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) { ushort gindex = glyphIndices[i]; - GlyphBitmap glyphBmp = GetGlyphBitmapFromBitmapFont(typeface, sizeInPoint, gindex); + GlyphBitmap glyphBmp = GetGlyphBitmapFromBitmapFont(gindex); if (glyphBmp == null) { - HandleNotFoundGlyph(gindex, _onEachGlyphDel); + + glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { @@ -491,7 +542,7 @@ void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) } } - else if (typeface.HasSvgTable()) + else if (_typeface.HasSvgTable()) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with TwitterEmoji @@ -506,10 +557,10 @@ void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) //TODO: add mutli-threads / async version ushort gindex = glyphIndices[i]; - GlyphBitmap glyphBmp = GetGlyphBitmapFromSvg(typeface, sizeInPoint, gindex); + GlyphBitmap glyphBmp = GetGlyphBitmapFromSvg(gindex); if (glyphBmp == null) { - HandleNotFoundGlyph(gindex, _onEachGlyphDel); + glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { @@ -564,7 +615,7 @@ void HandleNotFoundGlyph(ushort glyphIndex, OnEachGlyph onEachDel) { //build glyph ushort gindex = glyphIndices[i]; - outlineBuilder.BuildFromGlyphIndex(gindex, sizeInPoint); + outlineBuilder.BuildFromGlyphIndex(gindex, _sizeInPoints); BitmapAtlasItemSource glyphImg = aggTextureGen.CreateAtlasItem(outlineBuilder, 1); diff --git a/Typography.OpenFont/Typeface.cs b/Typography.OpenFont/Typeface.cs index 913bdb5d..56c21ddf 100644 --- a/Typography.OpenFont/Typeface.cs +++ b/Typography.OpenFont/Typeface.cs @@ -294,21 +294,34 @@ public short GetKernDistance(ushort leftGlyphIndex, ushort rightGlyphIndex) public short UnderlinePosition => PostTable.UnderlinePosition; //TODO: review here // - const int pointsPerInch = 72; //TODO: should be configurable + const int s_pointsPerInch = 72;//point per inch, fix? + + /// + /// default dpi + /// + public static uint DefaultDpi { get; set; } = 96; + /// /// convert from point-unit value to pixel value /// /// - /// + /// dpi /// - public static float ConvPointsToPixels(float targetPointSize, int resolution = 96) + public static float ConvPointsToPixels(float targetPointSize, int resolution = -1) { //http://stackoverflow.com/questions/139655/convert-pixels-to-points //points = pixels * 72 / 96 //------------------------------------------------ //pixels = targetPointSize * 96 /72 //pixels = targetPointSize * resolution / pointPerInch - return targetPointSize * resolution / pointsPerInch; + + if (resolution < 0) + { + //use current DefaultDPI + resolution = (int)DefaultDpi; + } + + return targetPointSize * resolution / s_pointsPerInch; } /// /// calculate scale to target pixel size based on current typeface's UnitsPerEm @@ -324,13 +337,19 @@ public float CalculateScaleToPixel(float targetPixelSize) /// calculate scale to target pixel size based on current typeface's UnitsPerEm /// /// target font size in point unit - /// + /// dpi /// - public float CalculateScaleToPixelFromPointSize(float targetPointSize, int resolution = 96) + public float CalculateScaleToPixelFromPointSize(float targetPointSize, int resolution = -1) { //1. var sizeInPixels = ConvPointsToPixels(sizeInPointUnit); - //2. return sizeInPixels / UnitsPerEm - return (targetPointSize * resolution / pointsPerInch) / this.UnitsPerEm; + //2. return sizeInPixels / UnitsPerEm + + if (resolution < 0) + { + //use current DefaultDPI + resolution = (int)DefaultDpi; + } + return (targetPointSize * resolution / s_pointsPerInch) / this.UnitsPerEm; } diff --git a/Typography.TextBreak/TextBreakerTest/Form1.cs b/Typography.TextBreak/TextBreakerTest/Form1.cs index 4d4a5c91..3815c6b2 100644 --- a/Typography.TextBreak/TextBreakerTest/Form1.cs +++ b/Typography.TextBreak/TextBreakerTest/Form1.cs @@ -1,4 +1,4 @@ -//MIT, 2016-2017, WinterDev +//MIT, 2016-present, WinterDev using System; //MIT, 2014-present, WinterDev using System.IO; diff --git a/Typography.TextBreak/TextBreakerTest/Program.cs b/Typography.TextBreak/TextBreakerTest/Program.cs index 4f8f2b71..9c29b24b 100644 --- a/Typography.TextBreak/TextBreakerTest/Program.cs +++ b/Typography.TextBreak/TextBreakerTest/Program.cs @@ -1,4 +1,4 @@ -//MIT, 2016-2017, WinterDev +//MIT, 2016-present, WinterDev using System; using System.Windows.Forms;