// public void CalculateUserCharGlyphAdvancePos(ref TextBufferSpan textBufferSpan, ILineSegmentList lineSegs, RequestFont font, int[] outputUserInputCharAdvance, out int outputTotalW, out int lineHeight) { //layout //from font //resolve for typeface // Typeface typeface = ResolveTypeface(font); _txtServices.SetCurrentFont(typeface, font.SizeInPoints); MyLineSegmentList mylineSegs = (MyLineSegmentList)lineSegs; float scale = typeface.CalculateScaleToPixelFromPointSize(font.SizeInPoints); outputTotalW = 0; int j = mylineSegs.Count; int pos = 0; //start at 0 _reusableTextBuffer.SetRawCharBuffer(textBufferSpan.GetRawCharBuffer()); for (int i = 0; i < j; ++i) { //get each segment MyLineSegment lineSeg = (MyLineSegment)mylineSegs.GetSegment(i); //each line seg may has different script lang _txtServices.CurrentScriptLang = lineSeg.scriptLang; // //CACHING ...., reduce number of GSUB/GPOS // //we cache used line segment for a while //we ask for caching context for a specific typeface and font size GlyphPlanSequence seq = _txtServices.GetUnscaledGlyphPlanSequence(_reusableTextBuffer, lineSeg.StartAt, lineSeg.Length); int seqLen = seq.Count; for (int s = 0; s < seqLen; ++s) { UnscaledGlyphPlan glyphPlan = seq[s]; double actualAdvX = glyphPlan.AdvanceX; outputTotalW += outputUserInputCharAdvance[pos + glyphPlan.input_cp_offset] += (int)Math.Round(actualAdvX * scale); } pos += lineSeg.Length; } // lineHeight = (int)Math.Round(typeface.CalculateRecommendLineSpacing() * scale); _reusableTextBuffer.SetRawCharBuffer(null); }
public static void CopyGlyphPlans(RenderVxFormattedString renderVx, UnscaledGlyphPlanList glyphPlans, float scale) { int n = glyphPlans.Count; //copy var renderVxGlyphPlans = new RenderVxGlyphPlan[n]; float acc_x = 0; float acc_y = 0; float x = 0; float y = 0; float g_x = 0; float g_y = 0; for (int i = 0; i < n; ++i) { UnscaledGlyphPlan glyphPlan = glyphPlans[i]; float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + ngx); //ideal x g_y = (float)(y + ngy); float g_w = (float)Math.Round(glyphPlan.AdvanceX * scale); acc_x += g_w; //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); renderVxGlyphPlans[i] = new RenderVxGlyphPlan( glyphPlan.glyphIndex, g_x, g_y, g_w ); } renderVx.glyphList = renderVxGlyphPlans; }
public void PrepareStringForRenderVx(RenderVxFormattedString renderVx, char[] buffer, int startAt, int len) { int j = buffer.Length; //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, font); float scale = _fontAtlas.TargetTextureScale; int recommendLineSpacing = _fontAtlas.OriginalRecommendLineSpacing; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again float x = 0; float y = 0; y -= ((recommendLineSpacing) * scale); renderVx.RecommmendLineSpacing = (int)(recommendLineSpacing * scale); // float scaleFromTexture = _finalTextureScale; TextureKind textureKind = _fontAtlas.TextureKind; //-------------------------- //TODO: review render steps //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos // ideal_x = (float)(x + (glyph.x * scale - glyphData.TextureXOffset) * scaleFromTexture); // ideal_y = (float)(y + (glyph.y * scale - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); //-------------------------- float g_x = 0; float g_y = 0; int baseY = (int)Math.Round(y); int n = glyphPlanSeq.len; int endBefore = glyphPlanSeq.startAt + n; //*** _glsx.SetAssociatedTextureInfo(_glBmp); // vboBufferList2.Clear(); indexList2.Clear(); float acc_x = 0; float acc_y = 0; for (int i = glyphPlanSeq.startAt; i < endBefore; ++i) { UnscaledGlyphPlanList glyphPlanList = GlyphPlanSequence.UnsafeGetInteralGlyphPlanList(glyphPlanSeq); UnscaledGlyphPlan glyph = glyphPlanList[i]; Typography.Rendering.TextureFontGlyphData glyphData; if (!_fontAtlas.TryGetGlyphDataByGlyphIndex(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- PixelFarm.Drawing.Rectangle srcRect = ConvToRect(glyphData.Rect); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + (ngx - glyphData.TextureXOffset) * scaleFromTexture); //ideal x g_y = (float)(y + (ngy - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); acc_x += (float)Math.Round(glyph.AdvanceX * scale); //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); switch (textureKind) { case TextureKind.Msdf: _glsx.DrawSubImageWithMsdf(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.StencilGreyScale: //stencil gray scale with fill-color _glsx.DrawGlyphImageWithStecil(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.Bitmap: _glsx.DrawSubImage(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.StencilLcdEffect: _glsx.WriteVboToList( vboBufferList2, indexList2, ref srcRect, g_x, g_y, scaleFromTexture); break; } } //--------- DrawingGL.GLRenderVxFormattedString renderVxFormattedString = (DrawingGL.GLRenderVxFormattedString)renderVx; renderVxFormattedString.IndexArray = indexList2.ToArray(); renderVxFormattedString.VertexCoords = vboBufferList2.ToArray(); renderVxFormattedString.VertexCount = indexList2.Count; }
public void DrawString(char[] buffer, int startAt, int len, double x, double y) { _glsx.FontFillColor = painter.FontFillColor; int j = buffer.Length; //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, font); float scale = _fontAtlas.TargetTextureScale; int recommendLineSpacing = _fontAtlas.OriginalRecommendLineSpacing; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again y -= ((_fontAtlas.OriginalRecommendLineSpacing) * scale); EnsureLoadGLBmp(); // float scaleFromTexture = _finalTextureScale; TextureKind textureKind = _fontAtlas.TextureKind; float g_x = 0; float g_y = 0; int baseY = (int)Math.Round(y); int n = glyphPlanSeq.len; int endBefore = glyphPlanSeq.startAt + n; //------------------------------------- _glsx.LoadTexture1(_glBmp); //------------------------------------- _vboBufferList.Clear(); //clear before use _indexList.Clear(); //clear before use float acc_x = 0; float acc_y = 0; for (int i = glyphPlanSeq.startAt; i < endBefore; ++i) { UnscaledGlyphPlanList glyphPlanList = GlyphPlanSequence.UnsafeGetInteralGlyphPlanList(glyphPlanSeq); UnscaledGlyphPlan glyph = glyphPlanList[i]; Typography.Rendering.TextureFontGlyphData glyphData; if (!_fontAtlas.TryGetGlyphDataByGlyphIndex(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- PixelFarm.Drawing.Rectangle srcRect = ConvToRect(glyphData.Rect); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + (ngx - glyphData.TextureXOffset) * scaleFromTexture); //ideal x g_y = (float)(y + (ngy - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); acc_x += (float)Math.Round(glyph.AdvanceX * scale); //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); switch (textureKind) { case TextureKind.Msdf: _glsx.DrawSubImageWithMsdf(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.StencilGreyScale: //stencil gray scale with fill-color _glsx.DrawGlyphImageWithStecil(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.Bitmap: _glsx.DrawSubImage(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case TextureKind.StencilLcdEffect: _glsx.WriteVboToList( _vboBufferList, _indexList, ref srcRect, g_x, g_y, scaleFromTexture); break; } } //------- //we create vbo first //then render _glsx.DrawGlyphImageWithSubPixelRenderingTechnique3(_vboBufferList.ToArray(), _indexList.ToArray()); }
/// <summary> /// generate glyph run into a given textRun /// </summary> /// <param name="outputTextRun"></param> /// <param name="charBuffer"></param> /// <param name="start"></param> /// <param name="len"></param> public void GenerateGlyphRuns(TextRun outputTextRun, char[] charBuffer, int start, int len) { // layout glyphs with selected layout technique float sizeInPoints = this.FontSizeInPoints; outputTextRun.typeface = this.Typeface; outputTextRun.sizeInPoints = sizeInPoints; //in this version we store original glyph into the mesh collection //and then we scale it later, so I just specific font size=0 (you can use any value) _glyphMeshCollection.SetCacheInfo(this.Typeface, 0, this.HintTechnique); GlyphLayoutMan.Typeface = this.Typeface; GlyphLayoutMan.Layout(charBuffer, start, len); float pxscale = this.Typeface.CalculateScaleToPixelFromPointSize(sizeInPoints); _resuableGlyphPlanList.Clear(); GenerateGlyphPlan(charBuffer, 0, charBuffer.Length, _resuableGlyphPlanList); // render each glyph int planCount = _resuableGlyphPlanList.Count; for (var i = 0; i < planCount; ++i) { _pathTranslator.Reset(); //---- //glyph path //---- UnscaledGlyphPlan glyphPlan = _resuableGlyphPlanList[i]; // //1. check if we have this glyph in cache? //if yes, not need to build it again ProcessedGlyph processGlyph; float[] tessData = null; if (!_glyphMeshCollection.TryGetCacheGlyph(glyphPlan.glyphIndex, out processGlyph)) { //if not found the create a new one and register it var writablePath = new WritablePath(); _pathTranslator.SetOutput(writablePath); _currentGlyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, sizeInPoints); _currentGlyphPathBuilder.ReadShapes(_pathTranslator); //------- //do tess int[] endContours; float[] flattenPoints = _curveFlattener.Flatten(writablePath._points, out endContours); tessData = _tessTool.TessAsTriVertexArray(flattenPoints, endContours, out int vertexCount); processGlyph = new ProcessedGlyph(tessData, (ushort)vertexCount); _glyphMeshCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, processGlyph); } outputTextRun.AddGlyph( new GlyphRun(glyphPlan, processGlyph.tessData, processGlyph.vertextCount)); } }
public void DrawString(Painter p, char[] buffer, int startAt, int len, double x, double y) { //if (_stencilBmp == null) //{ // //create a stencil bmp // _stencilBmp = new ActualBitmap(p.Width, p.Height); // _stencilBlender = new PixelProcessing.SubBitmapBlender(_stencilBmp, new PixelProcessing.PixelBlenderBGRA()); // _backPainter = AggPainter.Create(_stencilBmp); // //------ //} int j = buffer.Length; //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, _font); Typeface typeface = _textServices.ResolveTypeface(_font); float scale = typeface.CalculateScaleToPixelFromPointSize(_font.SizeInPoints); int recommendLineSpacing = (int)_font.LineSpacingInPixels; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again y -= _font.LineSpacingInPixels; // float scaleFromTexture = _finalTextureScale; TextureKind textureKind = _fontAtlas.TextureKind; float g_x = 0; float g_y = 0; int baseY = (int)Math.Round(y); //------------------------------------- //load texture //_pcx.LoadTexture1(_glBmp); //------------------------------------- float acc_x = 0; float acc_y = 0; p.DrawImage(_fontBmp, 100, 100); int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- int srcX, srcY, srcW, srcH; glyphData.GetRect(out srcX, out srcY, out srcW, out srcH); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + (ngx - glyphData.TextureXOffset) * scaleFromTexture); //ideal x g_y = (float)(y + (ngy - glyphData.TextureYOffset + srcH) * scaleFromTexture); acc_x += (float)Math.Round(glyph.AdvanceX * scale); //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); //p.RenderQuality = RenderQuality.Fast; //*** the atlas is inverted so... //p.DrawImage(_fontBmp, g_x, g_y, srcX, _fontBmp.Height - (srcY), srcW, srcH); //p.DrawImage(_fontBmp, g_x, g_y); //1. draw to back buffer //_backPainter.DrawImage(_fontBmp, g_x, g_y, srcX, _fontBmp.Height - (srcY), srcW, srcH); //2. then copy content to this //p.DrawImage(_stencilBmp, 100, 100); p.DrawImage(_fontBmp, g_x, g_y, srcX, _fontBmp.Height - (srcY + srcH), srcW, srcH); switch (textureKind) { default: break; case TextureKind.StencilLcdEffect: { } break; } //copy some part from the bitmap //switch (textureKind) //{ // case TextureKind.Msdf: // _pcx.DrawSubImageWithMsdf(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.StencilGreyScale: // //stencil gray scale with fill-color // _pcx.DrawGlyphImageWithStecil(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.Bitmap: // _pcx.DrawSubImage(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.StencilLcdEffect: // _pcx.WriteVboToList( // _vboBufferList, // _indexList, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; //} } //------- //we create vbo first //then render }
public override void DrawFromGlyphPlans(UnscaledGlyphPlanList glyphPlanList, int startAt, int len, float x, float y) { UpdateVisualOutputSettings(); //draw data in glyph plan //3. render each glyph float sizeInPoints = this.FontSizeInPoints; float scale = _currentTypeface.CalculateScaleToPixelFromPointSize(sizeInPoints); // _glyphMeshCollections.SetCacheInfo(this.Typeface, sizeInPoints, this.HintTechnique); //this draw a single line text span*** int endBefore = startAt + len; Graphics g = this.TargetGraphics; float acc_x = 0; float acc_y = 0; float g_x = 0; float g_y = 0; for (int i = startAt; i < endBefore; ++i) { UnscaledGlyphPlan glyphPlan = glyphPlanList[i]; //check if we have a cache of this glyph //if not -> create it GraphicsPath foundPath; if (!_glyphMeshCollections.TryGetCacheGlyph(glyphPlan.glyphIndex, out foundPath)) { //if not found then create a new one _currentGlyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, sizeInPoints); _txToGdiPath.Reset(); _currentGlyphPathBuilder.ReadShapes(_txToGdiPath); foundPath = _txToGdiPath.ResultGraphicsPath; //register _glyphMeshCollections.RegisterCachedGlyph(glyphPlan.glyphIndex, foundPath); } //------ //then move pen point to the position we want to draw a glyph float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * scale); g_x = (x + (ngx)); g_y = (y + (ngy)); acc_x += (float)Math.Round(glyphPlan.AdvanceX * scale); //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); g.TranslateTransform(g_x, g_y); if (FillBackground) { g.FillPath(_fillBrush, foundPath); } if (DrawOutline) { g.DrawPath(_outlinePen, foundPath); } //and then we reset back *** g.TranslateTransform(-g_x, -g_y); } }
public void MeasureString(char[] str, int startAt, int len, int limitWidth, out int charFit, out int charFitWidth) { //measure string if (str.Length < 1) { charFitWidth = 0; } _reusableMeasureBoxList.Clear(); //reset float pxscale = _currentTypeface.CalculateScaleToPixelFromPointSize(_fontSizeInPts); //NOET:at this moment, simple operation //may not be simple... //------------------- //input string may contain more than 1 script lang //user can parse it by other parser //but in this code, we use our Typography' parser //------------------- //user must setup the CustomBreakerBuilder before use int cur_startAt = startAt; float accumW = 0; foreach (BreakSpan breakSpan in BreakToLineSegments(str, startAt, len)) { //measure string at specific px scale _glyphLayout.Layout(str, breakSpan.startAt, breakSpan.len); // _reusableGlyphPlanList.Clear(); _glyphLayout.GenerateUnscaledGlyphPlans(_reusableGlyphPlanList); //measure ... //measure each glyph //limit at specific width int glyphCount = _reusableGlyphPlanList.Count; float acc_x = 0; //accum_x float acc_y = 0; //accum_y float g_x = 0; float g_y = 0; float x = 0; float y = 0; for (int i = 0; i < glyphCount; ++i) { UnscaledGlyphPlan glyphPlan = _reusableGlyphPlanList[i]; float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * pxscale); float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * pxscale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + (ngx)); //ideal x g_y = (float)(y + (ngy)); float g_w = (float)Math.Round(glyphPlan.AdvanceX * pxscale); acc_x += g_w; //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); float right = g_x + g_w; if (right >= accumW) { //stop here at this glyph charFit = i - 1; //TODO: review this charFitWidth = (int)System.Math.Round(accumW); return; } else { accumW = right; } } } charFit = 0; charFitWidth = 0; }
public void DrawString(char[] buffer, int startAt, int len, double left, double top) { _vboBuilder.Clear(); _vboBuilder.SetTextureInfo(_glBmp.Width, _glBmp.Height, _glBmp.IsYFlipped, _pcx.OriginKind); // _pcx.FontFillColor = _painter.FontFillColor; _pcx.LoadTexture(_glBmp); //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, _font); float px_scale = _px_scale; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again float scaleFromTexture = 1; TextureKind textureKind = _fontAtlas.TextureKind; float g_left = 0; float g_top = 0; int baseLine = (int)Math.Round((float)top + _font.AscentInPixels); int bottom = (int)Math.Round((float)top + _font.AscentInPixels - _font.DescentInPixels); float acc_x = 0; //local accumulate x float acc_y = 0; //local accumulate y #if DEBUG if (s_dbugShowMarkers) { if (s_dbugShowGlyphTexture) { //show original glyph texture at top _pcx.DrawImage(_glBmp, 0, 0); } //draw red-line-marker for baseLine _painter.StrokeColor = Color.Red; _painter.DrawLine(left, baseLine, left + 200, baseLine); // //draw magenta-line-marker for bottom line _painter.StrokeColor = Color.Magenta; int bottomLine = (int)Math.Round((float)top + _font.LineSpacingInPixels); _painter.DrawLine(left, bottomLine, left + 200, bottomLine); //draw blue-line-marker for top line _painter.StrokeColor = Color.Blue; _painter.DrawLine(0, top, left + 200, top); } DrawingTechnique = s_dbugDrawTechnique; //for debug only UseVBO = s_dbugUseVBO; //for debug only #endif int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; Typography.Rendering.TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- //paint src rect //temp fix, glyph texture img is not flipped //but the associate info is flipped => so //we need remap exact Y from the image Rectangle srcRect = new Rectangle(glyphData.Left, _glBmp.Height - (glyphData.Top + glyphData.Height), glyphData.Width, glyphData.Height); //offset length from 'base-line' float x_offset = acc_x + (float)Math.Round(glyph.OffsetX * px_scale - glyphData.TextureXOffset); float y_offset = acc_y + (float)Math.Round(glyph.OffsetY * px_scale - glyphData.TextureYOffset) + srcRect.Height; //*** //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_left = (float)(left + x_offset); g_top = (float)(bottom - y_offset); //*** acc_x += (float)Math.Round(glyph.AdvanceX * px_scale); //g_x = (float)Math.Round(g_x); //*** g_top = (float)Math.Floor(g_top);//adjust to integer num *** #if DEBUG if (s_dbugShowMarkers) { if (s_dbugShowGlyphTexture) { //draw yellow-rect-marker on original texture _painter.DrawRectangle(srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, Color.Yellow); } //draw debug-rect box at target glyph position _painter.DrawRectangle(g_left, g_top, srcRect.Width, srcRect.Height, Color.Black); _painter.StrokeColor = Color.Blue; //restore } #endif if (textureKind == TextureKind.Msdf) { _pcx.DrawSubImageWithMsdf(_glBmp, ref srcRect, g_left, g_top, scaleFromTexture); } else { switch (DrawingTechnique) { case GlyphTexturePrinterDrawingTechnique.Stencil: if (UseVBO) { _vboBuilder.WriteVboToList( ref srcRect, g_left, g_top); } else { //stencil gray scale with fill-color _pcx.DrawGlyphImageWithStecil(_glBmp, ref srcRect, g_left, g_top, scaleFromTexture); } break; case GlyphTexturePrinterDrawingTechnique.Copy: if (UseVBO) { _vboBuilder.WriteVboToList( ref srcRect, g_left, g_top); } else { _pcx.DrawSubImage(_glBmp, ref srcRect, g_left, g_top, 1); } break; case GlyphTexturePrinterDrawingTechnique.LcdSubPixelRendering: if (UseVBO) { _vboBuilder.WriteVboToList( ref srcRect, g_left, g_top); } else { _pcx.DrawGlyphImageWithSubPixelRenderingTechnique2_GlyphByGlyph( ref srcRect, g_left, g_top, 1); } break; } } } //------------------------------------------- // if (UseVBO) { switch (DrawingTechnique) { case GlyphTexturePrinterDrawingTechnique.Copy: _pcx.DrawGlyphImageWithCopy_VBO(_vboBuilder); break; case GlyphTexturePrinterDrawingTechnique.LcdSubPixelRendering: _pcx.DrawGlyphImageWithSubPixelRenderingTechnique3_DrawElements(_vboBuilder); break; case GlyphTexturePrinterDrawingTechnique.Stencil: _pcx.DrawGlyphImageWithStecil_VBO(_vboBuilder); break; } _vboBuilder.Clear(); } }
void RenderAndShowMeasureBox() { bool flipY = chkFlipY.Checked; //set some Gdi+ props... g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.Clear(Color.White); Typography.OpenFont.Typeface typeface = _currentTextPrinter.Typeface; Typography.OpenFont.TypefaceExtension2.UpdateAllCffGlyphBounds(typeface); float pxscale = typeface.CalculateScaleToPixelFromPointSize(_currentTextPrinter.FontSizeInPoints); int lineSpacing = (int)System.Math.Ceiling((double)typeface.CalculateLineSpacing(LineSpacingChoice.TypoMetric) * pxscale); if (flipY) { //credit: //http://stackoverflow.com/questions/1485745/flip-coordinates-when-drawing-to-control g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis g.TranslateTransform(0.0F, -500); // Translate the drawing area accordingly } //-------------------------------- //textspan measurement sample //-------------------------------- _currentTextPrinter.HintTechnique = (HintTechnique)lstHintList.SelectedItem; _currentTextPrinter.PositionTechnique = (PositionTechnique)cmbPositionTech.SelectedItem; _currentTextPrinter.UpdateGlyphLayoutSettings(); //render at specific pos float x_pos = 0, y_pos = lineSpacing * 2; char[] textBuffer = txtInputChar.Text.ToCharArray(); //Example 1: this is a basic draw sample _currentTextPrinter.FillColor = Color.Black; _currentTextPrinter.TargetGraphics = g; _currentTextPrinter.DrawString( textBuffer, 0, textBuffer.Length, x_pos, y_pos ); // //-------------------------------------------------- //Example 2: print glyph plan to 'user' list-> then draw it (or hold it/ not draw) //you can create you own class to hold userGlyphPlans.*** //2.1 _reusableUnscaledGlyphPlanList.Clear(); _currentTextPrinter.GenerateGlyphPlan(textBuffer, 0, textBuffer.Length, _reusableUnscaledGlyphPlanList); //2.2 //and we can print the formatted glyph plan later. y_pos -= lineSpacing;//next line _currentTextPrinter.FillColor = Color.Red; _currentTextPrinter.DrawFromGlyphPlans( new GlyphPlanSequence(_reusableUnscaledGlyphPlanList), x_pos, y_pos ); //Example 3: MeasureString UnscaledGlyphPlanList glyphPlans = new UnscaledGlyphPlanList(); _currentTextPrinter.GlyphLayoutMan.GenerateUnscaledGlyphPlans(glyphPlans); MeasuredStringBox strBox = _currentTextPrinter.GlyphLayoutMan.LayoutAndMeasureString( textBuffer, 0, textBuffer.Length, _currentTextPrinter.FontSizeInPoints); int j = glyphPlans.Count; float backup_xpos = x_pos; for (int i = 0; i < j; ++i) { UnscaledGlyphPlan glyphPlan = glyphPlans[i]; Typography.OpenFont.Glyph glyph = typeface.GetGlyphByIndex(glyphPlan.glyphIndex); // Typography.OpenFont.Bounds b = glyph.Bounds; // float xmin = b.XMin * pxscale; float ymin = b.YMin * pxscale; // float xmax = b.XMax * pxscale; float ymax = b.YMax * pxscale; // float glyph_x = x_pos + glyphPlan.OffsetX; g.DrawRectangle(Pens.Red, glyph_x + xmin, y_pos + ymin, xmax - xmin, ymax - ymin); x_pos += glyphPlan.AdvanceX * pxscale; } x_pos = backup_xpos; g.FillRectangle(Brushes.Red, new RectangleF(0, 0, 5, 5));//reference point(0,0) g.FillRectangle(Brushes.Green, new RectangleF(x_pos, y_pos, 3, 3)); float x_pos2 = x_pos + strBox.width + 10; g.DrawRectangle(Pens.Black, x_pos, y_pos + strBox.DescendingInPx, strBox.width, strBox.ClipHeightInPx); g.DrawRectangle(Pens.Red, x_pos, y_pos + strBox.DescendingInPx, strBox.width, strBox.LineSpaceInPx); g.DrawLine(Pens.Blue, x_pos, y_pos, x_pos2, y_pos); //baseline g.DrawLine(Pens.Green, x_pos, y_pos + strBox.DescendingInPx, x_pos2, y_pos + strBox.DescendingInPx); //descending g.DrawLine(Pens.Magenta, x_pos, y_pos + strBox.AscendingInPx, x_pos2, y_pos + strBox.AscendingInPx); //ascending ////------------ ////draw another line (for reference) y_pos -= lineSpacing;//next line _currentTextPrinter.FillColor = Color.Black; _currentTextPrinter.DrawFromGlyphPlans( new GlyphPlanSequence(_reusableUnscaledGlyphPlanList), x_pos, y_pos ); //transform back if (flipY) { g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis g.TranslateTransform(0.0F, -500); // Translate the drawing area accordingly } //--------- //txtMsgInfo.Text = "choice:" + choice.ToString() + "=" + lineSpacing.ToString(); }
public void DrawString(Painter p, char[] buffer, int startAt, int len, double x, double y) { //if (_stencilBmp == null) //{ // //create a stencil bmp // _stencilBmp = new ActualBitmap(p.Width, p.Height); // _stencilBlender = new PixelProcessing.SubBitmapBlender(_stencilBmp, new PixelProcessing.PixelBlenderBGRA()); // _backPainter = AggPainter.Create(_stencilBmp); // //------ //} int j = buffer.Length; //create temp buffer span that describe the part of a whole char buffer var textBufferSpan = new Typography.Text.TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _txtClient.CreateGlyphPlanSeq(textBufferSpan, _font); ResolvedFont resolvedFont = _textServices.ResolveFont(_font); Typeface typeface = resolvedFont.Typeface; float scale = typeface.CalculateScaleToPixelFromPointSize(_font.SizeInPoints); int recommendLineSpacing = resolvedFont.LineSpacingInPixels; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again y -= resolvedFont.LineSpacingInPixels; // float scaleFromTexture = _finalTextureScale; TextureKind textureKind = _fontAtlas.TextureKind; float g_x = 0; float g_y = 0; int baseY = (int)Math.Round(y); //------------------------------------- //load texture //_pcx.LoadTexture1(_glBmp); //------------------------------------- float acc_x = 0; float acc_y = 0; p.DrawImage(_fontBmp, 100, 100); int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; AtlasItem glyphData; if (!_fontAtlas.TryGetItem(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- int srcX, srcY, srcW, srcH; glyphData.GetRect(out srcX, out srcY, out srcW, out srcH); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_x = (float)(x + (ngx - glyphData.TextureXOffset) * scaleFromTexture); //ideal x g_y = (float)(y + (ngy - glyphData.TextureYOffset + srcH) * scaleFromTexture); acc_x += (float)Math.Round(glyph.AdvanceX * scale); //g_x = (float)Math.Round(g_x); g_y = (float)Math.Floor(g_y); p.DrawImage(_fontBmp, g_x, g_y, srcX, srcY, srcW, srcH); } //------- //we create vbo first //then render }
void RenderAndShowMeasureBox() { bool flipY = chkFlipY.Checked; //set some Gdi+ props... _g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; _g.Clear(Color.White); Typography.OpenFont.Typeface typeface = _currentTextPrinter.Typeface; Typography.OpenFont.Extensions.TypefaceExtensions.UpdateAllCffGlyphBounds(typeface); float pxscale = typeface.CalculateScaleToPixelFromPointSize(_currentTextPrinter.FontSizeInPoints); int lineSpacing = (int)System.Math.Ceiling((double)typeface.CalculateLineSpacing(LineSpacingChoice.TypoMetric) * pxscale); if (flipY) { //credit: //http://stackoverflow.com/questions/1485745/flip-coordinates-when-drawing-to-control _g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis _g.TranslateTransform(0.0F, -500); // Translate the drawing area accordingly } //-------------------------------- //textspan measurement sample //-------------------------------- _currentTextPrinter.HintTechnique = (HintTechnique)lstHintList.SelectedItem; _currentTextPrinter.PositionTechnique = (PositionTechnique)cmbPositionTech.SelectedItem; _currentTextPrinter.UpdateGlyphLayoutSettings(); //render at specific pos float x_pos = 0, y_pos = lineSpacing * 2; char[] textBuffer = txtInputChar.Text.ToCharArray(); //Example 1: this is a basic draw sample _currentTextPrinter.FillColor = Color.Black; _currentTextPrinter.TargetGraphics = _g; //_currentTextPrinter.DrawString( // textBuffer, // 0, // textBuffer.Length, // x_pos, // y_pos // ); // //-------------------------------------------------- //Example 2: print glyph plan to 'user' list-> then draw it (or hold it/ not draw) //you can create you own class to hold userGlyphPlans.*** //2.1 if (chkEnableMultiTypefaces.Checked) { _ftmGlyphPlansList.Clear(); _currentTextPrinter.GenerateGlyphPlans(textBuffer, 0, textBuffer.Length, _ftmGlyphPlansList); //2.2 //and we can print the formatted glyph plan later. y_pos -= lineSpacing;//next line _currentTextPrinter.FillColor = Color.Red; _currentTextPrinter.DrawFromFormattedGlyphPlans( _ftmGlyphPlansList, x_pos, y_pos ); //-------------------------------------------------- //Example 3: MeasureString //3.1 use data in formatted_glyph_plan_list to measure string => OK, OR //3.2 use another 'light-weight' text printer to measure string (without drawing actual string) //3.1 _currentTextPrinter.MeasureGlyphPlanList(_ftmGlyphPlansList, out int measureWidth); _g.DrawRectangle(Pens.Red, x_pos, y_pos, measureWidth, 30); float baseline = y_pos; //3.2 draw each glyph bounds { int m = _ftmGlyphPlansList.Count; for (int i = 0; i < m; ++i) { FormattedGlyphPlanSeq seq = _ftmGlyphPlansList[i]; ResolvedFont resolvedFont = seq.ResolvedFont; Typeface localTypeface = resolvedFont.Typeface; pxscale = resolvedFont.GetScaleToPixelFromPointInSize(); GlyphPlanSequence glyph_seq = seq.Seq; var snapToPxScale = new GlyphPlanSequenceSnapPixelScaleLayout(seq.Seq, 0, glyph_seq.Count, pxscale); x_pos += (resolvedFont.WhitespaceWidth * seq.PrefixWhitespaceCount); int nn = 0;/// while (snapToPxScale.Read()) { //float cx = (float)Math.Round(snapToPxScale.ExactX + x_pos); //float cy = (float)Math.Floor(snapToPxScale.ExactY + baseline); UnscaledGlyphPlan glyphPlan = glyph_seq[nn]; Glyph glyph = localTypeface.GetGlyph(glyphPlan.glyphIndex); Bounds b = glyph.Bounds; // float xmin = b.XMin * pxscale; float ymin = b.YMin * pxscale; // float xmax = b.XMax * pxscale; float ymax = b.YMax * pxscale; // float glyph_x = x_pos + (glyphPlan.OffsetX * pxscale); _g.DrawRectangle(Pens.Red, glyph_x + xmin, y_pos + ymin, xmax - xmin, ymax - ymin); x_pos += glyphPlan.AdvanceX * pxscale; nn++; } x_pos += (resolvedFont.WhitespaceWidth * seq.PostfixWhitespaceCount); } } _ftmGlyphPlansList.Clear(); } else { _reusableUnscaledGlyphPlanList.Clear(); _currentTextPrinter.GenerateGlyphPlans(textBuffer, 0, textBuffer.Length, _reusableUnscaledGlyphPlanList); //2.2 //and we can print the formatted glyph plan later. y_pos -= lineSpacing;//next line _currentTextPrinter.FillColor = Color.Red; // GlyphPlanSequence seq = new GlyphPlanSequence(_reusableUnscaledGlyphPlanList); _currentTextPrinter.DrawFromGlyphPlans( seq, x_pos, y_pos ); //-------------------------------------------------- //Example 3: MeasureString float descending_px = _currentTextPrinter.FontDescedingPx; float ascending_px = _currentTextPrinter.FontAscendingPx; float lineSpacing_px = _currentTextPrinter.FontLineSpacingPx; float clippedHeight_px = _currentTextPrinter.FontClipHeightPx; _currentTextPrinter.MeasureGlyphPlanSeq(seq, out int textspanW); UnscaledGlyphPlanList glyphPlans = new UnscaledGlyphPlanList(); _currentTextPrinter.GenerateGlyphPlans(textBuffer, 0, textBuffer.Length, glyphPlans); int j = glyphPlans.Count; float backup_xpos = x_pos; for (int i = 0; i < j; ++i) { UnscaledGlyphPlan glyphPlan = glyphPlans[i]; Glyph glyph = typeface.GetGlyph(glyphPlan.glyphIndex); // Bounds b = glyph.Bounds; // float xmin = b.XMin * pxscale; float ymin = b.YMin * pxscale; // float xmax = b.XMax * pxscale; float ymax = b.YMax * pxscale; // float glyph_x = x_pos + (glyphPlan.OffsetX * pxscale); _g.DrawRectangle(Pens.Red, glyph_x + xmin, y_pos + ymin, xmax - xmin, ymax - ymin); x_pos += glyphPlan.AdvanceX * pxscale; } x_pos = backup_xpos; _g.FillRectangle(Brushes.Red, new RectangleF(0, 0, 5, 5));//reference point(0,0) _g.FillRectangle(Brushes.Green, new RectangleF(x_pos, y_pos, 3, 3)); float x_pos2 = x_pos + textspanW + 10; _g.DrawRectangle(Pens.Black, x_pos, y_pos + descending_px, textspanW, clippedHeight_px); _g.DrawRectangle(Pens.Red, x_pos, y_pos + descending_px, textspanW, lineSpacing_px); _g.DrawLine(Pens.Blue, x_pos, y_pos, x_pos2, y_pos); //baseline _g.DrawLine(Pens.Green, x_pos, y_pos + descending_px, x_pos2, y_pos + descending_px); //descending _g.DrawLine(Pens.Magenta, x_pos, y_pos + ascending_px, x_pos2, y_pos + ascending_px); //ascending ////------------ ////draw another line (for reference) y_pos -= lineSpacing;//next line _currentTextPrinter.FillColor = Color.Black; _currentTextPrinter.DrawFromGlyphPlans( new GlyphPlanSequence(_reusableUnscaledGlyphPlanList), x_pos, y_pos ); } //transform back if (flipY) { _g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis _g.TranslateTransform(0.0F, -500); // Translate the drawing area accordingly } //--------- //txtMsgInfo.Text = "choice:" + choice.ToString() + "=" + lineSpacing.ToString(); }
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(); }
public override void DrawFromGlyphPlans(GlyphPlanSequence glyphPlanSeq, int startAt, int len, float left, float top) { Typeface typeface = _textServices.ResolveTypeface(_font); float scale = typeface.CalculateScaleToPixelFromPointSize(_font.SizeInPoints); int recommendLineSpacing = (int)_font.LineSpacingInPixels; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again // TextureKind textureKind = _fontAtlas.TextureKind; float gx = 0; float gy = 0; int baseY = (int)Math.Round(top); float acc_x = 0; float acc_y = 0; int lineHeight = (int)_font.LineSpacingInPixels; //temp PixelBlender32 prevPxBlender = _painter.DestBitmapBlender.OutputPixelBlender; //save _painter.DestBitmapBlender.OutputPixelBlender = _maskPixelBlenderPerCompo; //change to new blender bool fillGlyphByGlyph = true; if (fillGlyphByGlyph) { //test... //fill glyph-by-glyh var aaTech = this.AntialiasTech; int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan unscaledGlyphPlan = glyphPlanSeq[i]; TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(unscaledGlyphPlan.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- int srcX, srcY, srcW, srcH; glyphData.GetRect(out srcX, out srcY, out srcW, out srcH); float ngx = acc_x + (float)Math.Round(unscaledGlyphPlan.OffsetX * scale); float ngy = acc_y + (float)Math.Round(unscaledGlyphPlan.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- //if (glyphData.TextureXOffset != 0) //{ //} gx = (float)(left + (ngx - glyphData.TextureXOffset)); //ideal x gy = (float)(top + (ngy + glyphData.TextureYOffset - srcH + lineHeight)); acc_x += (float)Math.Round(unscaledGlyphPlan.AdvanceX * scale); gy = (float)Math.Floor(gy);// + lineHeight; //clear with solid black color //_maskBufferPainter.Clear(Color.Black); //clear mask buffer at specific pos _maskBufferPainter.FillRect(gx - 1, gy - 1, srcW + 2, srcH + 2, Color.Black); //draw 'stencil' glyph on mask-buffer _maskBufferPainter.DrawImage(_fontBmp, gx, gy, srcX, _fontBmp.Height - (srcY + srcH), srcW, srcH); switch (aaTech) { default: { //select component to render this need to render 3 times for lcd technique //1. B _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.B; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.B; _painter.FillRect(gx + 1, gy, srcW, srcH); //2. G _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.G; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.G; _painter.FillRect(gx + 1, gy, srcW, srcH); //3. R _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.R; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.R; _painter.FillRect(gx + 1, gy, srcW, srcH); } break; case AntialiasTechnique.GreyscaleStencil: { //fill once //we choose greeh channel (middle) _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.G; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.EnableAll; _painter.FillRect(gx + 1, gy, srcW, srcH); } break; } } } else { //clear entire line _maskBufferPainter.FillRect(gx - 1, gy - 1, _maskBufferPainter.Width - gx + 2, lineHeight + 2, Color.Black); bool isFirst = true; int startX = 0, startY = 0; float lenW = 0; float lenH = 0; int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- int srcX, srcY, srcW, srcH; glyphData.GetRect(out srcX, out srcY, out srcW, out srcH); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- gx = (float)(left + (ngx - glyphData.TextureXOffset)); //ideal x gy = (float)(top + (ngy - glyphData.TextureYOffset - srcH + lineHeight)); acc_x += (float)Math.Round(glyph.AdvanceX * scale); gy = (float)Math.Floor(gy) + lineHeight; if (isFirst) { startX = (int)gx; startY = (int)gy; isFirst = false; } _maskBufferPainter.DrawImage(_fontBmp, gx + 1, gy, srcX, _fontBmp.Height - (srcY), srcW + 1, srcH); lenW = gx + srcW; if (srcH > lenH) { lenH = srcH; } } //-------------------------- //fill color on 'stencil' mask { //select component to render this need to render 3 times for lcd technique //1. B _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.B; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.B; _painter.FillRect(startX + 1, startY, lenW, lenH); //2. G _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.G; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.G; _painter.FillRect(startX + 1, startY, lenW, lenH); //3. R _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.R; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.R; _painter.FillRect(startX + 1, startY, lenW, lenH); } } // _painter.DestBitmapBlender.OutputPixelBlender = prevPxBlender;//restore back }
private void cmdMeasureTextSpan_Click(object sender, System.EventArgs e) { //set some Gdi+ props... g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; g.Clear(Color.White); //credit: //http://stackoverflow.com/questions/1485745/flip-coordinates-when-drawing-to-control g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis g.TranslateTransform(0.0F, -(float)300); // Translate the drawing area accordingly //-------------------------------- //textspan measurement sample //-------------------------------- _currentTextPrinter.HintTechnique = (HintTechnique)lstHintList.SelectedItem; _currentTextPrinter.PositionTechnique = (PositionTechnique)cmbPositionTech.SelectedItem; //render at specific pos float x_pos = 0, y_pos = 100; char[] textBuffer = txtInputChar.Text.ToCharArray(); //Example 1: this is a basic draw sample _currentTextPrinter.FillColor = Color.Black; _currentTextPrinter.TargetGraphics = g; _currentTextPrinter.DrawString( textBuffer, 0, textBuffer.Length, x_pos, y_pos ); // //-------------------------------------------------- //Example 2: print glyph plan to 'user' list-> then draw it (or hold it/ not draw) //you can create you own class to hold userGlyphPlans.*** //2.1 _reusableUnscaledGlyphPlanList.Clear(); _currentTextPrinter.GenerateGlyphPlan(textBuffer, 0, textBuffer.Length, _reusableUnscaledGlyphPlanList); //2.2 //and we can print the formatted glyph plan later. y_pos -= _currentTextPrinter.FontLineSpacingPx; _currentTextPrinter.FillColor = Color.Red; _currentTextPrinter.DrawFromGlyphPlans( new GlyphPlanSequence(_reusableUnscaledGlyphPlanList), x_pos, y_pos ); //Example 3: MeasureString Typography.OpenFont.Typeface typeface = _currentTextPrinter.Typeface; UnscaledGlyphPlanList userGlyphPlans = new UnscaledGlyphPlanList(); _currentTextPrinter.GlyphLayoutMan.GenerateUnscaledGlyphPlans(userGlyphPlans); MeasuredStringBox strBox = new MeasuredStringBox(); throw new System.NotSupportedException(); //_currentTextPrinter.GlyphLayoutMan.LayoutAndMeasureString( // textBuffer, 0, textBuffer.Length, // _currentTextPrinter.FontSizeInPoints, // true, // userGlyphPlans); float x_pos2 = x_pos + strBox.width + 10; g.DrawRectangle(Pens.Red, x_pos, y_pos + strBox.descending, strBox.width, strBox.CalculateLineHeight()); g.DrawLine(Pens.Blue, x_pos, y_pos, x_pos2, y_pos); //baseline g.DrawLine(Pens.Green, x_pos, y_pos + strBox.descending, x_pos2, y_pos + strBox.descending); //descending g.DrawLine(Pens.Magenta, x_pos, y_pos + strBox.ascending, x_pos2, y_pos + strBox.ascending); //ascending Typography.OpenFont.TypefaceExtension2.UpdateAllCffGlyphBounds(typeface); float pxscale = typeface.CalculateScaleToPixelFromPointSize(_currentTextPrinter.FontSizeInPoints); int j = userGlyphPlans.Count; for (int i = 0; i < j; ++i) { UnscaledGlyphPlan glyphPlan = userGlyphPlans[i]; Typography.OpenFont.Glyph glyph = typeface.GetGlyphByIndex(glyphPlan.glyphIndex); // Typography.OpenFont.Bounds b = glyph.Bounds; // float xmin = b.XMin * pxscale; float ymin = b.YMin * pxscale; // float xmax = b.XMax * pxscale; float ymax = b.YMax * pxscale; // float glyph_x = x_pos + glyphPlan.OffsetX; g.DrawRectangle(Pens.Red, glyph_x + xmin, y_pos + ymin, xmax - xmin, ymax - ymin); } //------------ _currentTextPrinter.FillColor = Color.Black; //transform back g.ScaleTransform(1.0F, -1.0F); // Flip the Y-Axis g.TranslateTransform(0.0F, -(float)300); // Translate the drawing area accordingly }
public void CalculateUserCharGlyphAdvancePos(ref TextBufferSpan textBufferSpan, ILineSegmentList lineSegs, RequestFont font, int[] outputUserInputCharAdvance, out int outputTotalW, out int lineHeight) { //layout //from font //resolve for typeface // Typeface typeface = ResolveTypeface(font); _txtServices.SetCurrentFont(typeface, font.SizeInPoints); MyLineSegmentList mylineSegs = (MyLineSegmentList)lineSegs; float scale = typeface.CalculateScaleToPixelFromPointSize(font.SizeInPoints); outputTotalW = 0; int j = mylineSegs.Count; int pos = 0; //start at 0 _reusableTextBuffer.SetRawCharBuffer(textBufferSpan.GetRawCharBuffer()); for (int i = 0; i < j; ++i) { //userGlyphPlanList.Clear(); //userCharToGlyphMapList.Clear(); //get each segment MyLineSegment lineSeg = mylineSegs.GetSegment(i); //each line seg may has different script lang _txtServices.CurrentScriptLang = lineSeg.scriptLang; // //CACHING ...., reduce number of GSUB/GPOS // //we cache used line segment for a while //we ask for caching context for a specific typeface and font size GlyphPlanSequence seq = _txtServices.GetUnscaledGlyphPlanSequence(_reusableTextBuffer, lineSeg.StartAt, lineSeg.Length); //IMPORTANT //num of glyph may more or less than original user input char buffer // //float g_x = 0; //float g_y = 0; //int baseY = (int)Math.Round(y); //int n = glyphPlanSeq.len; //int endBefore = glyphPlanSeq.startAt + n; ////*** //_glsx.SetAssociatedTextureInfo(_glBmp); //List<float> vboBufferList = new List<float>(); //List<ushort> indexList = new List<ushort>(); //for (int i = glyphPlanSeq.startAt; i < endBefore; ++i) //{ // GlyphPlanList glyphPlanList = GlyphPlanSequence.UnsafeGetInteralGlyphPlanList(glyphPlanSeq); // GlyphPlan glyph = glyphPlanList[i]; // Typography.Rendering.TextureGlyphMapData glyphData; // if (!_fontAtlas.TryGetGlyphDataByCodePoint(glyph.glyphIndex, out glyphData)) // { // //if no glyph data, we should render a missing glyph *** // continue; // } // //if (scaleFromTexture != 1) // //{ // //} // //-------------------------------------- // //TODO: review precise height in float // //-------------------------------------- // PixelFarm.Drawing.Rectangle srcRect = ConvToRect(glyphData.Rect); // g_x = (float)(x + (glyph.ExactX * scale - glyphData.TextureXOffset) * scaleFromTexture); //ideal x // g_y = (float)(y + (glyph.ExactY * scale - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); // //for sharp glyph // //we adjust g_x,g_y to integer value // //float g_y2 = (float)Math.Floor(g_y); // g_x = (float)Math.Round(g_x); // g_y = (float)Math.Floor(g_y); // switch (textureKind) // { // case TextureKind.Msdf: // _glsx.DrawSubImageWithMsdf(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.StencilGreyScale: // //stencil gray scale with fill-color // _glsx.DrawGlyphImageWithStecil(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.Bitmap: // _glsx.DrawSubImage(_glBmp, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // case TextureKind.StencilLcdEffect: // _glsx.WriteVboToList( // vboBufferList, // indexList, // ref srcRect, // g_x, // g_y, // scaleFromTexture); // break; // } //} ////--------- int seqLen = seq.Count; for (int s = 0; s < seqLen; ++s) { UnscaledGlyphPlan glyphPlan = seq[s]; double actualAdvX = glyphPlan.AdvanceX; outputTotalW += outputUserInputCharAdvance[pos + glyphPlan.input_cp_offset] += (int)Math.Round(actualAdvX * scale); } pos += lineSeg.Length; } // lineHeight = (int)Math.Round(typeface.CalculateRecommendLineSpacing() * scale); _reusableTextBuffer.SetRawCharBuffer(null); }
public void Append(UnscaledGlyphPlan glyphPlan) { _glyphPlans.Add(glyphPlan); }
public void PrepareStringForRenderVx(GLRenderVxFormattedString renderVxFormattedString, char[] buffer, int startAt, int len) { int top = 0; //simulate top int left = 0; //simulate left _vboBuilder.Clear(); _vboBuilder.SetTextureInfo(_glBmp.Width, _glBmp.Height, _glBmp.IsYFlipped, _pcx.OriginKind); //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, _font); float px_scale = _px_scale; //-------------------------- TextureKind textureKind = _fontAtlas.TextureKind; float g_left = 0; float g_top = 0; int baseLine = (int)Math.Round((float)top + _font.AscentInPixels); int bottom = (int)Math.Round((float)top + _font.AscentInPixels - _font.DescentInPixels); float acc_x = 0; //local accumulate x float acc_y = 0; //local accumulate y int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; Typography.Rendering.TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- //paint src rect //temp fix, glyph texture img is not flipped //but the associate info is flipped => so //we need remap exact Y from the image Rectangle srcRect = new Rectangle(glyphData.Left, _glBmp.Height - (glyphData.Top + glyphData.Height), glyphData.Width, glyphData.Height); //offset length from 'base-line' float x_offset = acc_x + (float)Math.Round(glyph.OffsetX * px_scale - glyphData.TextureXOffset); float y_offset = acc_y + (float)Math.Round(glyph.OffsetY * px_scale - glyphData.TextureYOffset) + srcRect.Height; //*** //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- g_left = (float)(left + x_offset); g_top = (float)(bottom - y_offset); //*** acc_x += (float)Math.Round(glyph.AdvanceX * px_scale); //g_x = (float)Math.Round(g_x); //*** g_top = (float)Math.Floor(g_top);//adjust to integer num *** // _vboBuilder.WriteVboToList(ref srcRect, g_left, g_top); } //--- //copy vbo result and store into renderVx float[] vertexList = _vboBuilder._buffer.ToArray(); ushort[] indexList = _vboBuilder._indexList.ToArray(); //--- renderVxFormattedString.IndexArrayCount = _vboBuilder._indexList.Count; renderVxFormattedString.IndexArray = _vboBuilder._indexList.ToArray(); renderVxFormattedString.VertexCoords = _vboBuilder._buffer.ToArray(); _vboBuilder.Clear(); }
public void DrawString(Painter p, char[] buffer, int startAt, int len, double x, double y) { AggPainter painter = p as AggPainter; if (painter == null) { return; } // int width = painter.Width; int height = painter.Height; if (!_pixelBlenderSetup) { SetupMaskPixelBlender(width, height); _pixelBlenderSetup = true; } int j = buffer.Length; //create temp buffer span that describe the part of a whole char buffer TextBufferSpan textBufferSpan = new TextBufferSpan(buffer, startAt, len); //ask text service to parse user input char buffer and create a glyph-plan-sequence (list of glyph-plan) //with specific request font GlyphPlanSequence glyphPlanSeq = _textServices.CreateGlyphPlanSeq(ref textBufferSpan, _font); float scale = 1;// _fontAtlas.TargetTextureScale; int recommendLineSpacing = (int)_font.LineSpacingInPixels; //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again y -= _font.LineSpacingInPixels; // float scaleFromTexture = _finalTextureScale; TextureKind textureKind = _fontAtlas.TextureKind; float gx = 0; float gy = 0; int baseY = (int)Math.Round(y); float acc_x = 0; float acc_y = 0; int lineHeight = (int)_font.LineSpacingInPixels;//temp //painter.DestBitmapBlender.OutputPixelBlender = maskPixelBlenderPerCompo; //change to new blender painter.DestBitmapBlender.OutputPixelBlender = _maskPixelBlenderPerCompo; //change to new blender int seqLen = glyphPlanSeq.Count; for (int i = 0; i < seqLen; ++i) { UnscaledGlyphPlan glyph = glyphPlanSeq[i]; TextureGlyphMapData glyphData; if (!_fontAtlas.TryGetGlyphMapData(glyph.glyphIndex, out glyphData)) { //if no glyph data, we should render a missing glyph *** continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- int srcX, srcY, srcW, srcH; glyphData.GetRect(out srcX, out srcY, out srcW, out srcH); float ngx = acc_x + (float)Math.Round(glyph.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyph.OffsetY * scale); //NOTE: // -glyphData.TextureXOffset => restore to original pos // -glyphData.TextureYOffset => restore to original pos //-------------------------- gx = (float)(x + (ngx - glyphData.TextureXOffset) * scaleFromTexture); //ideal x gy = (float)(y + (ngy - glyphData.TextureYOffset - srcH + lineHeight) * scaleFromTexture); acc_x += (float)Math.Round(glyph.AdvanceX * scale); gy = (float)Math.Floor(gy) + lineHeight; //clear with solid black color //_maskBufferPainter.Clear(Color.Black); _maskBufferPainter.FillRect(gx - 1, gy - 1, srcW + 2, srcH + 2, Color.Black); //draw 'stencil' glyph on mask-buffer _maskBufferPainter.DrawImage(_fontBmp, gx, gy, srcX, _fontBmp.Height - (srcY), srcW, srcH); //select component to render this need to render 3 times for lcd technique //1. B _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.B; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.B; painter.FillRect(gx + 1, gy, srcW, srcH); //2. G _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.G; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.G; painter.FillRect(gx + 1, gy, srcW, srcH); //3. R _maskPixelBlenderPerCompo.SelectedMaskComponent = PixelBlenderColorComponent.R; _maskPixelBlenderPerCompo.EnableOutputColorComponent = EnableOutputColorComponent.R; painter.FillRect(gx + 1, gy, srcW, srcH); } }
public override void DrawFromGlyphPlans(UnscaledGlyphPlanList glyphPlanList, int startAt, int len, float x, float y) { Painter painter = this.TargetCanvasPainter; if (StartDrawOnLeftTop) { //version 2 //offset y down y += this.FontLineSpacingPx; } //Typeface typeface = _glyphPathBuilder.Typeface; //3. layout glyphs with selected layout technique //TODO: review this again, we should use pixel? float fontSizePoint = this.FontSizeInPoints; float scale = _currentTypeface.CalculateScaleToPixelFromPointSize(fontSizePoint); //4. render each glyph float ox = painter.OriginX; float oy = painter.OriginY; int endBefore = startAt + len; Typography.OpenFont.Tables.COLR colrTable = _currentTypeface.COLRTable; Typography.OpenFont.Tables.CPAL cpalTable = _currentTypeface.CPALTable; bool hasColorGlyphs = (colrTable != null) && (cpalTable != null); //--------------------------------------------------- _glyphMeshStore.SetFont(_currentTypeface, fontSizePoint); //--------------------------------------------------- float g_x = 0; float g_y = 0; float baseY = (int)y; if (!hasColorGlyphs) { bool savedUseLcdMode = painter.UseSubPixelLcdEffect; //save,restore later RenderQualtity savedRederQuality = painter.RenderQuality; painter.RenderQuality = RenderQualtity.HighQuality; painter.UseSubPixelLcdEffect = true; Agg.Transform.Affine flipY = Agg.Transform.Affine.NewMatix( Agg.Transform.AffinePlan.Scale(1, -1)); //flip Y VertexStore reusableVxs = new VertexStore(); float acc_x = 0; //acummulate x float acc_y = 0; //acummulate y for (int i = startAt; i < endBefore; ++i) { //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- UnscaledGlyphPlan glyphPlan = glyphPlanList[i]; float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * scale); acc_x += (float)Math.Round(glyphPlan.AdvanceX * scale); g_x = ngx; g_y = ngy; painter.SetOrigin(g_x, g_y); //----------------------------------- //invert each glyph //version 3: reusableVxs.Clear(); VertexStore vxs = _glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex); flipY.TransformToVxs(vxs, reusableVxs); painter.Fill(reusableVxs); //version2; //VertexStore vsx = _glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex); //_vxs1 = _invertY.TransformToVxs(vsx, _vxs1); //painter.Fill(_vxs1); //_vxs1.Clear(); //version1 //painter.Fill(_glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex)); } //restore painter.RenderQuality = savedRederQuality; painter.UseSubPixelLcdEffect = savedUseLcdMode; } else { //------------- //this glyph has color information //------------- Color originalFillColor = painter.FillColor; float acc_x = 0; float acc_y = 0; for (int i = startAt; i < endBefore; ++i) { UnscaledGlyphPlan glyphPlan = glyphPlanList[i]; float ngx = acc_x + (float)Math.Round(glyphPlan.OffsetX * scale); float ngy = acc_y + (float)Math.Round(glyphPlan.OffsetY * scale); g_x = ngx; g_y = ngy; acc_x += (float)Math.Round(glyphPlan.AdvanceX * scale); painter.SetOrigin(g_x, g_y); //----------------------------------- ushort colorLayerStart; if (colrTable.LayerIndices.TryGetValue(glyphPlan.glyphIndex, out colorLayerStart)) { //TODO: optimize this //we found color info for this glyph ushort colorLayerCount = colrTable.LayerCounts[glyphPlan.glyphIndex]; byte r, g, b, a; for (int c = colorLayerStart; c < colorLayerStart + colorLayerCount; ++c) { ushort gIndex = colrTable.GlyphLayers[c]; int palette = 0; // FIXME: assume palette 0 for now cpalTable.GetColor( cpalTable.Palettes[palette] + colrTable.GlyphPalettes[c], //index out r, out g, out b, out a); //----------- painter.FillColor = new Color(r, g, b);//? a component painter.Fill(_glyphMeshStore.GetGlyphMesh(gIndex)); } } else { //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- painter.Fill(_glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex)); } } painter.FillColor = originalFillColor; //restore color } //restore prev origin painter.SetOrigin(ox, oy); }