public void MeasureString(char[] buffer, int startAt, int len, out int w, out int h) { UpdateGlyphLayoutSettings(); _glyphMeshStore.SetFont(_currentTypeface, this.FontSizeInPoints); _glyphMeshStore.SimulateOblique = this.SimulateSlant; TextBufferSpan textBuffSpan = new TextBufferSpan(buffer, startAt, len); Size s = _textServices.MeasureString(ref textBuffSpan, _painter.CurrentFont); w = s.Width; h = s.Height; }
public void DrawString(RenderVxFormattedString renderVx, double x, double y) { //TODO: review here float ox = _painter.OriginX; float oy = _painter.OriginY; //1. update some props.. //2. update current type face UpdateGlyphLayoutSettings(); _glyphMeshStore.SetFont(_currentTypeface, this.FontSizeInPoints); _glyphMeshStore.SimulateOblique = this.SimulateSlant; //3. layout glyphs with selected layout technique //TODO: review this again, we should use pixel? float fontSizePoint = this.FontSizeInPoints; float scale = _currentTypeface.CalculateScaleToPixelFromPointSize(fontSizePoint); RenderVxGlyphPlan[] glyphPlans = renderVx.glyphList; int j = glyphPlans.Length; //--------------------------------------------------- //consider use cached glyph, to increase performance //GlyphPosPixelSnapKind x_snap = this.GlyphPosPixelSnapX; //GlyphPosPixelSnapKind y_snap = this.GlyphPosPixelSnapY; float g_x = 0; float g_y = 0; float baseY = (int)y; for (int i = 0; i < j; ++i) { RenderVxGlyphPlan glyphPlan = glyphPlans[i]; //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- VertexStore vxs = _glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex); g_x = (float)(glyphPlan.x * scale + x); g_y = (float)glyphPlan.y * scale; _painter.SetOrigin(g_x, g_y); _painter.Fill(vxs); } //restore prev origin _painter.SetOrigin(ox, oy); }
protected override void SetGlyphVxs(GlyphBox glyphBox, Typeface typeface, float sizeInPoint) { if (glyphBox is VxsGlyphBox vxsGlyphBox) { _glyphMeshStore.SetFont(typeface, sizeInPoint);//20= font size _glyphMeshStore.FlipGlyphUpward = true; if (glyphBox.IsItalic) { _glyphMeshStore.SimulateOblique = glyphBox.IsItalic; } vxsGlyphBox.GlyphVxs = _glyphMeshStore.GetGlyphMesh(glyphBox.GlyphIndex); if (_glyphMeshStore.SimulateOblique) { _glyphMeshStore.SimulateOblique = false; } } }
private void TxtTestGlyph_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode != Keys.Enter) { return; } //--- string text = txtTestGlyph.Text.Trim(); if (text.Length == 0) { return; } char singleChar = text[0]; //1. read a single glyph from font file. //2. generate vxs from the glyph. var glyphMeshStore = new GlyphMeshStore(); glyphMeshStore.FlipGlyphUpward = false; //just example, not need to open-read everytime. Typeface typeface = null; //string fontpath = "Samples/SourceSansPro-Regular.ttf"; string fontpath = "Samples/Roboto-Regular.ttf"; using (FileStream fs = new FileStream(fontpath, FileMode.Open, FileAccess.Read)) { OpenFontReader fontReader = new OpenFontReader(); typeface = fontReader.Read(fs); glyphMeshStore.SetFont(typeface, 56); ushort glyphIndex = typeface.GetGlyphIndex(singleChar); VertexStore glyphVxs = glyphMeshStore.GetNewUnFlattenVxs(glyphIndex); //** //VertexStore glyphVxs = glyphMeshStore.GetGlyphMesh(glyphIndex);//org GenerateMsdf(glyphVxs); } }
private void button3_Click(object sender, EventArgs e) { //it should be faster if we use 'mesh' cache //instead of read-transform it every time like code above(button2_click) LoadFont(); float font_size_in_Point = 20; _glyphMeshStore.SetFont(_latinModernMathFont, font_size_in_Point);//20= font size _glyphMeshStore.FlipGlyphUpward = true; float px_scale = _latinModernMathFont.CalculateScaleToPixelFromPointSize(font_size_in_Point); using (Tools.BorrowAggPainter(_memBmp, out var p)) { p.Clear(PixelFarm.Drawing.Color.White); float prevX = p.OriginX; float prevY = p.OriginY; int line_left = 10; int line_top = 50; p.SetOrigin(line_left, line_top);//*** test //draw reference point p.FillRect(0, 0, 5, 5, PixelFarm.Drawing.Color.Red); char[] test_str = "‽_x‾".ToCharArray(); int inline_left = 0; int inline_top = 0; //---------- GlyphLayout glyphLayout = new GlyphLayout(); glyphLayout.ScriptLang = new ScriptLang("math"); glyphLayout.Typeface = _latinModernMathFont; //temp fix for some typeface glyphLayout.SetGlyphIndexNotFoundHandler((glyph_layout, codepoint, next_codepoint) => { switch (codepoint) { //overline unicode case 8254: return(2246); //overline-combine, this will break into 3 parts in math layout process } return(0); }); // glyphLayout.Layout(test_str, 0, test_str.Length); List <UnscaledGlyphPlan> glyphPlans = new List <UnscaledGlyphPlan>(); foreach (UnscaledGlyphPlan glypyPlan in glyphLayout.GetUnscaledGlyphPlanIter()) { glyphPlans.Add(glypyPlan); } //-------- for (int i = 0; i < glyphPlans.Count; ++i) { //ushort glyphIndex = _latinModernMathFont.GetGlyphIndex((int)test_str[i]); ////do some glyph-substitution //ushort advW = _latinModernMathFont.GetAdvanceWidth((int)test_str[i]);//unscale glyph width //now scale it to specific font size UnscaledGlyphPlan glyphPlan = glyphPlans[i]; int advW_s = (int)System.Math.Round(px_scale * glyphPlan.AdvanceX); VertexStore v1 = _glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex); p.SetOrigin(line_left + inline_left, line_top + inline_top); p.Fill(v1, PixelFarm.Drawing.Color.Black); inline_left += advW_s;//move } //restore p.SetOrigin(prevX, prevY); } //----------- CopyBitmapToScreen(); }
void CreateTextureFontFromGlyphIndices( HintTechnique hintTechnique, SimpleBitmapAtlasBuilder atlasBuilder, ushort[] glyphIndices) { //sample: create sample msdf texture //------------------------------------------------------------- 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... //please note that DPI effect glyph size //*** int tmpMemBmpHeight = (int)(2 * _typeface.CalculateRecommendLineSpacing() * _px_scale); // if (atlasBuilder.TextureKind == TextureKind.Msdf) { var msdfGenParams = new Msdfgen.MsdfGenParams(); int j = glyphIndices.Length; if (MsdfGenVersion == 3) { Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.BuildFromGlyphIndex(gindex, -1, glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); // atlasBuilder.AddItemSource(glyphImg); } } } else { //use gen3 Msdfgen.MsdfGen3 gen3 = new Msdfgen.MsdfGen3(); for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; //create picture with unscaled version set scale=-1 //(we will create glyph contours and analyze them) var glyphToVxs = new GlyphTranslatorToVxs(); outlineBuilder.BuildFromGlyphIndex(gindex, -1, glyphToVxs); using (Tools.BorrowVxs(out var vxs)) { glyphToVxs.WriteUnFlattenOutput(vxs, _px_scale); BitmapAtlasItemSource glyphImg = gen3.GenerateMsdfTexture(vxs); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } return; } else if (atlasBuilder.TextureKind == TextureKind.Bitmap) { //generate color bitmap atlas int j = glyphIndices.Length; GlyphMeshStore glyphMeshStore = new GlyphMeshStore(); glyphMeshStore.SetFont(_typeface, _sizeInPoints); aggTextureGen.TextureKind = TextureKind.Bitmap; using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square { aggTextureGen.Painter = PixelFarm.CpuBlit.AggPainter.Create(tmpMemBmp); #if DEBUG tmpMemBmp._dbugNote = "CreateGlyphImage()"; #endif 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)) { //not found, then render as normal //TODO: impl //create glyph img glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { //TODO: review this again GlyphBitmap glyphBmp = GetGlyphBitmapFromColorOutlineGlyph(gindex, glyphMeshStore, colorLayerStart); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } } } else if (_typeface.IsBitmapFont) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with Noto Color Emoji for (int i = 0; i < j; ++i) { ushort gindex = glyphIndices[i]; GlyphBitmap glyphBmp = GetGlyphBitmapFromBitmapFont(gindex); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } } else if (_typeface.HasSvgTable()) { aggTextureGen.TextureKind = TextureKind.Bitmap; //test this with TwitterEmoji //generate membitmap from svg #if DEBUG System.Diagnostics.Stopwatch sw1 = new System.Diagnostics.Stopwatch(); sw1.Start(); #endif for (int i = 0; i < j; ++i) { //TODO: add mutli-threads / async version ushort gindex = glyphIndices[i]; GlyphBitmap glyphBmp = GetGlyphBitmapFromSvg(gindex); if (glyphBmp == null) { glyphNotFoundHelper.HandleNotFoundGlyph(gindex); } else { int w = glyphBmp.Width; int h = glyphBmp.Height; BitmapAtlasItemSource glyphImage = new BitmapAtlasItemSource(glyphBmp.Width, glyphBmp.Height); glyphImage.TextureXOffset = (short)glyphBmp.ImageStartX; glyphImage.TextureYOffset = (short)glyphBmp.ImageStartY; // glyphImage.SetImageBuffer(MemBitmapExt.CopyImgBuffer(glyphBmp.Bitmap, w, h, true), false); glyphImage.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImage); atlasBuilder.AddItemSource(glyphImage); //clear glyphBmp.Bitmap.Dispose(); glyphBmp.Bitmap = null; } } #if DEBUG sw1.Stop(); long ms = sw1.ElapsedMilliseconds; #endif } return; //NO go below //*** } //END using } //--------------------------- //OTHERS.... { aggTextureGen.TextureKind = atlasBuilder.TextureKind; //create glyph img using (PixelFarm.CpuBlit.MemBitmap tmpMemBmp = new PixelFarm.CpuBlit.MemBitmap(tmpMemBmpHeight, tmpMemBmpHeight)) //square { //draw a glyph into tmpMemBmp and then copy to a GlyphImage aggTextureGen.Painter = PixelFarm.CpuBlit.AggPainter.Create(tmpMemBmp); #if DEBUG tmpMemBmp._dbugNote = "CreateGlyphImage()"; #endif int j = glyphIndices.Length; for (int i = 0; i < j; ++i) { //build glyph ushort gindex = glyphIndices[i]; outlineBuilder.BuildFromGlyphIndex(gindex, _sizeInPoints); BitmapAtlasItemSource glyphImg = aggTextureGen.CreateAtlasItem(outlineBuilder, 1); glyphImg.UniqueInt16Name = gindex; _onEachGlyphDel?.Invoke(glyphImg); atlasBuilder.AddItemSource(glyphImg); } } } }