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(); GlyphLayoutExtensions.GenerateGlyphPlans( _glyphLayout.ResultUnscaledGlyphPositions, pxscale, true, _reusableGlyphPlanList); //measure each glyph //limit at specific width int glyphCount = _reusableGlyphPlanList.Count; for (int i = 0; i < glyphCount; ++i) { GlyphPlan glyphPlan = _reusableGlyphPlanList[i]; float right = glyphPlan.ExactRight * pxscale; 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 override void DrawFromGlyphPlans(List <GlyphPlan> 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.CalculateToPixelScaleFromPointSize(sizeInPoints); // _glyphMeshCollections.SetCacheInfo(this.Typeface, sizeInPoints, this.HintTechnique); //this draw a single line text span*** int endBefore = startAt + len; Graphics g = this.TargetGraphics; for (int i = startAt; i < endBefore; ++i) { GlyphPlan 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 tx = x + glyphPlan.ExactX; float ty = y + glyphPlan.ExactY; g.TranslateTransform(tx, ty); if (FillBackground) { g.FillPath(_fillBrush, foundPath); } if (DrawOutline) { g.DrawPath(_outlinePen, foundPath); } //and then we reset back *** g.TranslateTransform(-tx, -ty); } }
/// <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.CurrentTypeFace; 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.CurrentTypeFace, 0, this.HintTechnique); outputGlyphPlans.Clear(); glyphLayout.Typeface = this.CurrentTypeFace; glyphLayout.GenerateGlyphPlans(charBuffer, start, len, outputGlyphPlans, null); // render each glyph int planCount = outputGlyphPlans.Count; for (var i = 0; i < planCount; ++i) { pathTranslator.Reset(); //---- //glyph path //---- GlyphPlan glyphPlan = outputGlyphPlans[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); int nTessElems; tessData = _tessTool.TessPolygon(flattenPoints, endContours, out nTessElems); //------- processGlyph = new ProcessedGlyph(tessData, (ushort)nTessElems); _glyphMeshCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, processGlyph); } outputTextRun.AddGlyph( new GlyphRun(glyphPlan, processGlyph.tessData, processGlyph.tessNElements)); } }
public void DrawString(char[] text, int startAt, int len, double x, double y) { float ox = canvasPainter.OriginX; float oy = canvasPainter.OriginY; //1. update some props.. //2. update current type face UpdateTypefaceAndGlyphBuilder(); 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 = typeface.CalculateToPixelScaleFromPointSize(fontSizePoint); _outputGlyphPlans.Clear(); _glyphLayout.Layout(typeface, text, startAt, len, _outputGlyphPlans); //4. render each glyph int j = _outputGlyphPlans.Count; //--------------------------------------------------- //consider use cached glyph, to increase performance hintGlyphCollection.SetCacheInfo(typeface, fontSizePoint, this.HintTechnique); //--------------------------------------------------- for (int i = 0; i < j; ++i) { GlyphPlan glyphPlan = _outputGlyphPlans[i]; //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- VertexStore glyphVxs; if (!hintGlyphCollection.TryGetCacheGlyph(glyphPlan.glyphIndex, out glyphVxs)) { //if not found then create new glyph vxs and cache it _glyphPathBuilder.SetHintTechnique(this.HintTechnique); _glyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, fontSizePoint); //----------------------------------- _tovxs.Reset(); _glyphPathBuilder.ReadShapes(_tovxs); //TODO: review here, //float pxScale = _glyphPathBuilder.GetPixelScale(); glyphVxs = new VertexStore(); _tovxs.WriteOutput(glyphVxs, _vxsPool); // hintGlyphCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, glyphVxs); } canvasPainter.SetOrigin((float)(glyphPlan.x * scale + x), (float)(glyphPlan.y * scale + y)); canvasPainter.Fill(glyphVxs); } //restore prev origin canvasPainter.SetOrigin(ox, oy); }
public override void DrawGlyphPlanList(List <GlyphPlan> glyphPlanList, int startAt, int len, float xpos, float ypos) { CanvasPainter canvasPainter = this.TargetCanvasPainter; 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 = typeface.CalculateFromPointToPixelScale(fontSizePoint); //4. render each glyph float ox = canvasPainter.OriginX; float oy = canvasPainter.OriginY; int endBefore = startAt + len; //--------------------------------------------------- //consider use cached glyph, to increase performance hintGlyphCollection.SetCacheInfo(typeface, fontSizePoint, this.HintTechnique); //--------------------------------------------------- for (int i = startAt; i < endBefore; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- VertexStore glyphVxs; if (!hintGlyphCollection.TryGetCacheGlyph(glyphPlan.glyphIndex, out glyphVxs)) { //if not found then create new glyph vxs and cache it _glyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, fontSizePoint); //----------------------------------- _tovxs.Reset(); _glyphPathBuilder.ReadShapes(_tovxs); //TODO: review here, //float pxScale = _glyphPathBuilder.GetPixelScale(); glyphVxs = new VertexStore(); _tovxs.WriteOutput(glyphVxs, _vxsPool); // hintGlyphCollection.RegisterCachedGlyph(glyphPlan.glyphIndex, glyphVxs); } canvasPainter.SetOrigin((float)(glyphPlan.x * scale + xpos), (float)(glyphPlan.y * scale + ypos)); canvasPainter.Fill(glyphVxs); } //restore prev origin canvasPainter.SetOrigin(ox, oy); }
public void Draw() { GlyphPlanList glyphPlans = _line._glyphPlans; List <UserCharToGlyphIndexMap> userCharToGlyphIndexMap = _line._userCharToGlyphMap; if (_line.ContentChanged) { //re-calculate char[] textBuffer = _line._charBuffer.ToArray(); glyphPlans.Clear(); userCharToGlyphIndexMap.Clear(); //read glyph plan and userCharToGlyphIndexMap _printer.GenerateGlyphPlan(textBuffer, 0, textBuffer.Length, glyphPlans, userCharToGlyphIndexMap); toPxScale = _printer.Typeface.CalculateScaleToPixelFromPointSize(_printer.FontSizeInPoints); _line.ContentChanged = false; } if (glyphPlans.Count > 0) { _printer.DrawFromGlyphPlans(glyphPlans, X, Y); //draw caret //not blink in this version int caret_index = _line.CaretCharIndex; //find caret pos based on glyph plan //TODO: check when do gsub (glyph number may not match with user char number) if (caret_index == 0) { _printer.DrawCaret(X, this.Y); } else { UserCharToGlyphIndexMap map = userCharToGlyphIndexMap[caret_index - 1]; GlyphPlan p = glyphPlans[map.glyphIndexListOffset_plus1 + map.len - 2]; _printer.DrawCaret(X + p.ExactRight, this.Y); } } else { _printer.DrawCaret(X, this.Y); } }
public void DoLeft() { int count = _charBuffer.Count; if (count == 0) { _caretCharIndex = 0; return; } else if (_caretCharIndex > 0) { //this is on the end _caretCharIndex--; //check if the caret can rest on this glyph? if (_caretCharIndex > 0) { //find its mapping to glyph index UserCharToGlyphIndexMap userCharToGlyphMap = _userCharToGlyphMap[_caretCharIndex]; int mapToGlyphIndex = userCharToGlyphMap.glyphIndexListOffset_plus1; // if (mapToGlyphIndex == 0) { //no map DoLeft(); //recursive *** return; } //------------------------- //we -1 *** GlyphPlan glyphPlan = _glyphPlans[userCharToGlyphMap.glyphIndexListOffset_plus1 - 1]; if (glyphPlan.advX <= 0) { //caret can't rest here //so DoLeft(); //recursive *** return; } //--------------------- // } } else { } }
public static void CopyGlyphPlans(RenderVxFormattedString renderVx, GlyphPlanList glyphPlans, float scale) { int n = glyphPlans.Count; //copy var renderVxGlyphPlans = new RenderVxGlyphPlan[n]; for (int i = 0; i < n; ++i) { GlyphPlan glyphPlan = glyphPlans[i]; renderVxGlyphPlans[i] = new RenderVxGlyphPlan( glyphPlan.glyphIndex, glyphPlan.ExactX, glyphPlan.ExactY, glyphPlan.AdvanceX ); } renderVx.glyphList = renderVxGlyphPlans; }
public static void CopyGlyphPlans(RenderVxFormattedString renderVx, List <GlyphPlan> glyphPlans, float scale) { int n = glyphPlans.Count; //copy var renderVxGlyphPlans = new RenderVxGlyphPlan[n]; for (int i = 0; i < n; ++i) { GlyphPlan glyphPlan = glyphPlans[i]; renderVxGlyphPlans[i] = new RenderVxGlyphPlan( glyphPlan.glyphIndex, glyphPlan.x * scale, glyphPlan.y * scale, glyphPlan.advX * scale ); } renderVx.glyphList = renderVxGlyphPlans; }
protected override void GetGlyphPosImpl(ActualFont actualFont, char[] buffer, int startAt, int len, List <GlyphPlan> glyphPlans) { NativeFont nativeFont = actualFont as NativeFont; if (nativeFont == null) { nativeFont = ResolveForNativeFont(actualFont); } unsafe { //TODO: review proper array size here int lim = len * 2; ProperGlyph *properGlyphArray = stackalloc ProperGlyph[lim]; fixed(char *head = &buffer[0]) { //we use font shaping engine here NativeMyFontsLib.MyFtShaping( nativeFont.NativeFontFace.HBFont, head, buffer.Length, properGlyphArray); } //copy from proper glyph to //create glyph plan for (int i = 0; i < lim; ++i) { ProperGlyph propGlyph = properGlyphArray[i]; if (propGlyph.codepoint == 0) { //finish , just return return; } GlyphPlan plan = new GlyphPlan((ushort)propGlyph.codepoint); plan.advX = propGlyph.x_advance; glyphPlans.Add(plan); } } }
public void DoRight() { int count = _charBuffer.Count; if (count == 0) { return; } else if (_caretCharIndex < count) { //this is on the end _caretCharIndex++; //check if the caret can rest on this glyph? if (_caretCharIndex < count) { //find its mapping to glyph index UserCharToGlyphIndexMap userCharToGlyphMap = _userCharToGlyphMap[_caretCharIndex]; int mapToGlyphIndex = userCharToGlyphMap.glyphIndexListOffset_plus1; // if (mapToGlyphIndex == 0) { //no map DoRight(); //recursive *** return; } //------------------------- //we -1 *** GlyphPlan glyphPlan = _glyphPlans[userCharToGlyphMap.glyphIndexListOffset_plus1 - 1]; if (!glyphPlan.AdvanceMoveForward) { //caret can't rest here //so DoRight(); //recursive *** return; } } } else { } }
public override void DrawGlyphPlanList(List <GlyphPlan> glyphPlanList, int startAt, int len, float x, float y) { UpdateVisualOutputSettings(); //draw data in glyph plan //3. render each glyph System.Drawing.Drawing2D.Matrix scaleMat = null; float sizeInPoints = this.FontSizeInPoints; float scale = _currentTypeface.CalculateFromPointToPixelScale(sizeInPoints); //this draw a single line text span*** int endBefore = startAt + len; Graphics g = this.TargetGraphics; for (int i = startAt; i < endBefore; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; _currentGlyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, sizeInPoints); // scaleMat = new System.Drawing.Drawing2D.Matrix( 1, 0, //scale x 0, 1, //scale y x + glyphPlan.x * scale, y + glyphPlan.y * scale //xpos,ypos ); // _txToGdiPath.Reset(); _currentGlyphPathBuilder.ReadShapes(_txToGdiPath); System.Drawing.Drawing2D.GraphicsPath path = _txToGdiPath.ResultGraphicsPath; path.Transform(scaleMat); if (FillBackground) { g.FillPath(_fillBrush, path); } if (DrawOutline) { g.DrawPath(_outlinePen, path); } } }
public void DrawString(char[] text, double x, double y) { glyphPlanList.Clear(); RequestFont currentFont = canvasPainter.CurrentFont; vxsTextPrinter.Print(currentFont.SizeInPoints, text, glyphPlanList); int glyphListLen = glyphPlanList.Count; float ox = canvasPainter.OriginX; float oy = canvasPainter.OriginY; for (int i = 0; i < glyphListLen; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; canvasPainter.SetOrigin((float)(glyphPlan.x + x), (float)(glyphPlan.y + y)); canvasPainter.Fill((VertexStore)glyphPlan.vxs); } canvasPainter.SetOrigin(ox, oy); }
List <ushort> inputGlyphs = new List <ushort>(); //not thread safe*** public void Print(Typeface typeface, float size, char[] str, List <GlyphPlan> glyphPlanBuffer) { //1. layout _glyphLayout.Layout(typeface, size, str, glyphPlanBuffer); var glyphPathBuilder = new MyGlyphPathBuilder(typeface); int j = glyphPlanBuffer.Count; float pxScale = typeface.CalculateFromPointToPixelScale(size); for (int i = 0; i < j; ++i) { GlyphPlan glyphPlan = glyphPlanBuffer[i]; //----------------------------------- //check if we static vxs/bmp for this glyph //if not, create and cache //----------------------------------- glyphPathBuilder.BuildFromGlyphIndex(glyphPlan.glyphIndex, size); //----------------------------------- var vxsBuilder = new GlyphPathBuilderVxs(); glyphPathBuilder.ReadShapes(vxsBuilder); glyphPlan.vxs = vxsBuilder.GetVxs(pxScale); } }
public override void DrawFromGlyphPlans(GlyphPlanList glyphPlanList, int startAt, int len, float x, float y) { CanvasPainter canvasPainter = this.TargetCanvasPainter; //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 = canvasPainter.OriginX; float oy = canvasPainter.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) { 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? //----------------------------------- GlyphPlan glyphPlan = glyphPlanList[i]; g_x = glyphPlan.ExactX + x; g_y = glyphPlan.ExactY + y; canvasPainter.SetOrigin(g_x, g_y); //----------------------------------- canvasPainter.Fill(_glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex)); } } else { //------------- //this glyph has color information //------------- Color originalFillColor = canvasPainter.FillColor; for (int i = startAt; i < endBefore; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; g_x = glyphPlan.ExactX + x; g_y = glyphPlan.ExactY + y; canvasPainter.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); //----------- canvasPainter.FillColor = new Color(r, g, b);//? a component canvasPainter.Fill(_glyphMeshStore.GetGlyphMesh(gIndex)); } } else { //----------------------------------- //TODO: review here *** //PERFORMANCE revisit here //if we have create a vxs we can cache it for later use? //----------------------------------- canvasPainter.Fill(_glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex)); } } canvasPainter.FillColor = originalFillColor; //restore color } //restore prev origin canvasPainter.SetOrigin(ox, oy); }
public void DrawString(char[] buffer, int startAt, int len, double x, double y) { int j = buffer.Length; //resolve font from painter? glyphPlans.Clear(); _glyphLayout.Layout(_typeface, buffer, startAt, len, glyphPlans); float scale = _typeface.CalculateToPixelScaleFromPointSize(font.SizeInPoints); //-------------------------- //TODO: //if (x,y) is left top //we need to adjust y again y -= (_typeface.Ascender - _typeface.Descender + _typeface.LineGap) * scale; int n = glyphPlans.Count; EnsureLoadGLBmp(); // float scaleFromTexture = _finalTextureScale; Typography.Rendering.TextureKind textureKind = simpleFontAtlas.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); //-------------------------- GlyphPosPixelSnapKind x_snap = this.GlyphPosPixelSnapX; GlyphPosPixelSnapKind y_snap = this.GlyphPosPixelSnapY; float g_x = 0; float g_y = 0; int baseY = (int)Math.Round(y); for (int i = 0; i < n; ++i) { GlyphPlan glyph = glyphPlans[i]; Typography.Rendering.TextureFontGlyphData glyphData; if (!simpleFontAtlas.TryGetGlyphDataByCodePoint(glyph.glyphIndex, out glyphData)) { continue; } //-------------------------------------- //TODO: review precise height in float //-------------------------------------- PixelFarm.Drawing.Rectangle srcRect = ConvToRect(glyphData.Rect); switch (x_snap) { default: throw new NotSupportedException(); case GlyphPosPixelSnapKind.Integer: { g_x = (float)(x + (glyph.x * scale - glyphData.TextureXOffset) * scaleFromTexture); //ideal x int floor_x = (int)g_x; //round to int 0,1 if (g_x - floor_x >= (1f / 2f)) { g_x = floor_x + 1; } else { g_x = floor_x; } } break; case GlyphPosPixelSnapKind.Half: { g_x = (float)(x + (glyph.x * scale - glyphData.TextureXOffset) * scaleFromTexture); //ideal x //adjust int floor_x = (int)g_x; //round to int 0, 0.5,1.0 if (g_x - floor_x >= (2f / 3f)) { g_x = floor_x + 1; } else if (g_x - floor_x >= (1f / 3f)) { g_x = floor_x + 0.5f; } else { g_x = floor_x; } } break; case GlyphPosPixelSnapKind.None: g_x = (float)(x + (glyph.x * scale - glyphData.TextureXOffset) * scaleFromTexture); break; } // switch (y_snap) { default: throw new NotSupportedException(); case GlyphPosPixelSnapKind.Integer: //use baseY not y { g_y = (float)((glyph.y * scale - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); int floor_y = (int)g_y; //round to int 0,1 if (g_y - floor_y >= (1f / 2f)) { g_y = floor_y + 1; } else { g_y = floor_y; } g_y = baseY + g_y; } break; case GlyphPosPixelSnapKind.Half: //review here //use baseY not y { g_y = (float)((glyph.y * scale - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); int floor_y = (int)g_y; //round to int 0, 0.5,1.0 if (g_y - floor_y >= (2f / 3f)) { g_y = floor_y + 1; } else if (g_x - floor_y >= (1f / 3f)) { g_y = floor_y + 0.5f; } else { g_y = floor_y; } g_y = baseY + g_y; } break; case GlyphPosPixelSnapKind.None: //use Y not baseY g_y = (float)(y + (glyph.y * scale - glyphData.TextureYOffset + srcRect.Height) * scaleFromTexture); break; } switch (textureKind) { case Typography.Rendering.TextureKind.Msdf: canvas2d.DrawSubImageWithMsdf(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case Typography.Rendering.TextureKind.AggGrayScale: canvas2d.DrawSubImage(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; case Typography.Rendering.TextureKind.AggSubPixel: canvas2d.DrawGlyphImageWithSubPixelRenderingTechnique(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; } } }
void RenderWithTextPrinterAndMiniAgg(string fontfile, string str, float sizeInPoint, int resolution) { //1. if (printer2 == null) { printer2 = new TextPrinter(); printer2.ScriptLang = ScriptLangs.Thai; } printer2.FontFile = fontfile; // printer2.EnableLigature = this.chkGsubEnableLigature.Checked; printer2.PositionTechnique = (PositionTecnhique)cmbPositionTech.SelectedItem; //printer.EnableTrueTypeHint = this.chkTrueTypeHint.Checked; //printer.UseAggVerticalHinting = this.chkVerticalHinting.Checked; // int len = str.Length; // List <GlyphPlan> glyphPlanList = new List <GlyphPlan>(len); printer2.Print(sizeInPoint, str, glyphPlanList); //-------------------------- //5. use PixelFarm's Agg to render to bitmap... //5.1 clear background p.Clear(PixelFarm.Drawing.Color.White); //--------------------------- //TODO: review here //fake subpixel rendering //not correct p.UseSubPixelRendering = chkLcdTechnique.Checked; //--------------------------- if (chkFillBackground.Checked) { //5.2 p.FillColor = PixelFarm.Drawing.Color.Black; //5.3 int glyphListLen = glyphPlanList.Count; float ox = p.OriginX; float oy = p.OriginY; float cx = 0; float cy = 10; for (int i = 0; i < glyphListLen; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; cx = glyphPlan.x; cy = glyphPlan.y; p.SetOrigin(cx, cy); p.Fill((VertexStore)glyphPlan.vxs); } p.SetOrigin(ox, oy); } if (chkBorder.Checked) { //5.4 p.StrokeColor = PixelFarm.Drawing.Color.Green; //user can specific border width here... //p.StrokeWidth = 2; //5.5 int glyphListLen = glyphPlanList.Count; float ox = p.OriginX; float oy = p.OriginY; float cx = 0; float cy = 10; for (int i = 0; i < glyphListLen; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; cx = glyphPlan.x; p.SetOrigin(cx, cy); p.Draw((VertexStore)glyphPlan.vxs); } p.SetOrigin(ox, oy); } //6. use this util to copy image from Agg actual image to System.Drawing.Bitmap PixelFarm.Agg.Imaging.BitmapHelper.CopyToGdiPlusBitmapSameSize(destImg, winBmp); //--------------- //7. just render our bitmap g.Clear(Color.White); g.DrawImage(winBmp, new Point(10, 0)); //-------------------------- }
public override void DrawFromGlyphPlans(GlyphPlanList 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; 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? //----------------------------------- GlyphPlan glyphPlan = glyphPlanList[i]; g_x = glyphPlan.ExactX + x; g_y = glyphPlan.ExactY + y; painter.SetOrigin(g_x, g_y); //----------------------------------- //invert each glyph //version 3: painter.Fill(_glyphMeshStore.GetGlyphMesh(glyphPlan.glyphIndex)); //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; for (int i = startAt; i < endBefore; ++i) { GlyphPlan glyphPlan = glyphPlanList[i]; g_x = glyphPlan.ExactX + x; g_y = glyphPlan.ExactY + y; 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); }
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); _typographyTxtServices.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 _typographyTxtServices.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 = _typographyTxtServices.GetUnscaledGlyphPlanSequence(_reusableTextBuffer, lineSeg.StartAt, lineSeg.Length); GlyphPlanList planList = GlyphPlanSequence.UnsafeGetInteralGlyphPlanList(seq); //IMPORTANT //num of glyph may more or less than original user input char buffer // int endAt = seq.startAt + seq.len; int seq_startAt = seq.startAt; for (int s = seq_startAt; s < endAt; ++s) { GlyphPlan glyphPlan = planList[s]; float tx = glyphPlan.ExactX; float ty = glyphPlan.ExactY; 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 DrawString(char[] buffer, double x, double y) { int j = buffer.Length; int buffsize = j * 2; //resolve font from painter? ActualFont fontImp = ff.GetFontAtPointsSize(font.SizeInPoints); var tt = (Typography.OpenFont.Typeface)ff.GetInternalTypeface(); List <GlyphPlan> glyphPlans = new List <GlyphPlan>(); _glyphLayout.Layout(tt, font.SizeInPoints, buffer, glyphPlans); // //un-test version //ActualFont fontImp = nativeFontStore.GetResolvedNativeFont(painter.CurrentFont); //if (properGlyphs == null) //{ // properGlyphs = new ProperGlyph[buffsize]; // TextShapingService.GetGlyphPos(fontImp, buffer, 0, buffsize, properGlyphs); //} //TODO: implement msdf texture //double xpos = x; //for (int i = 0; i < buffsize; ++i) //{ // uint codepoint = properGlyphs[i].codepoint; // if (codepoint == 0) // { // break; // } // //------------------------------------------------------------- // FontGlyph glyph = fontImp.GetGlyphByIndex(codepoint); // //glyph image32 // //------------------------------------------------------------- // GLBitmap bmp = new GLBitmap(new LazyAggBitmapBufferProvider(glyph.glyphImage32)); // var left = glyph.glyphMatrix.img_horiBearingX; // this.canvas2d.DrawImage(bmp, // (float)(xpos + (left >> 6)), // (float)(y + (glyph.glyphMatrix.bboxYmin >> 6))); // int w = (glyph.glyphMatrix.advanceX) >> 6; // xpos += (w); // bmp.Dispose(); //temp here // //------------------------------------------------------------- //} //------------------------------------- //msdf texture version double xpos = x; int n = glyphPlans.Count; Typography.Rendering.GlyphImage glyphImage = simpleFontAtlas.TotalGlyph; GLBitmap glBmp = new GLBitmap(glyphImage.Width, glyphImage.Height, glyphImage.GetImageBuffer(), false); float c_x = (float)x; float c_y = (float)y; //int left = ((int)(glyph.glyphMatrix.img_horiBearingX * scale) >> 6); int left = 0; //float baseline = c_y - 24;//eg line height= 24 //create a list float baseline = c_y - 24;//eg line height= 24 //create a list bool isFlipY = canvas2d.FlipY; if (!isFlipY) { canvas2d.FlipY = true; } for (int i = 0; i < n; ++i) { GlyphPlan glyph = glyphPlans[i]; Typography.Rendering.TextureFontGlyphData glyphData; if (!simpleFontAtlas.GetRectByCodePoint(glyph.glyphIndex, out glyphData)) { //Rectangle r = glyphData.Rect; //float x_min = glyphData.BBoxXMin / 64; ////draw each glyph at specific position ////_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); //_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); ////c_x += r.Width - 10; //c_x += (glyphData.AdvanceX / 64); continue; } //found PixelFarm.Drawing.Rectangle r = ConvToRect(glyphData.Rect); //test draw full msdf gen img //canvas2d.DrawImage(glBmp, c_x + left, (float)(baseline + ((int)(glyphData.ImgHeight)))); canvas2d.DrawSubImageWithMsdf(glBmp, ref r, c_x + left, (float)(baseline + ((int)(glyphData.ImgHeight))), 1.0f); c_x += glyph.advX; } canvas2d.FlipY = isFlipY; glBmp.Dispose(); //temp here //draw with texture printer *** //char[] chars = text.ToCharArray(); //int j = chars.Length; //int buffsize = j * 2; ////get kerning list ////get actual font for this canvas //TextureFont currentFont = _currentTextureFont; //SimpleFontAtlas fontAtlas = currentFont.FontAtlas; //ProperGlyph[] properGlyphs = new ProperGlyph[buffsize]; //TextShapingService.GetGlyphPos(currentFont, chars, 0, buffsize, properGlyphs); //GLBitmap glBmp = (GLBitmap)currentFont.GLBmp; //if (glBmp == null) //{ // //create glbmp // GlyphImage glyphImage = fontAtlas.TotalGlyph; // int[] buffer = glyphImage.GetImageBuffer(); // glBmp = new GLBitmap(glyphImage.Width, glyphImage.Height, buffer, false); //} ////int j = chars.Length; //// //float c_x = (float)x; //float c_y = (float)y; ////TODO: review here *** ////----------------- ////1. layout each glyph before render *** //// //float baseline = c_y - 24;//eg line height= 24 //create a list ////-------------- //List<float> coords = new List<float>(); //float scale = 1f; //for (int i = 0; i < buffsize; ++i) //{ // ProperGlyph glyph1 = properGlyphs[i]; // uint codepoint = properGlyphs[i].codepoint; // if (codepoint == 0) // { // break; // } // //-------------------------------- // //if (codepoint == 1173 && i > 1) // //{ // // //check prev code point // // codepoint = 1168; // //} // //-------------------------------- // TextureFontGlyphData glyphData; // if (!fontAtlas.GetRectByCodePoint((int)codepoint, out glyphData)) // { // //Rectangle r = glyphData.Rect; // //float x_min = glyphData.BBoxXMin / 64; // ////draw each glyph at specific position // ////_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); // //_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); // ////c_x += r.Width - 10; // //c_x += (glyphData.AdvanceX / 64); // continue; // } // FontGlyph glyph = currentFont.GetGlyphByIndex(codepoint); // int left = ((int)(glyph.glyphMatrix.img_horiBearingX * scale) >> 6); // Rectangle r = glyphData.Rect; // int adjustX = 0; // int bboxYMin = glyph.glyphMatrix.bboxYmin >> 6; // if (bboxYMin > 1 || bboxYMin < -1) // { // // adjustX = 3; // } // //scale down 0.8; // //_canvas.DrawSubImageWithMsdf(glBmp, ref r, adjustX + c_x + left, // // (float)(baseline + ((int)(glyphData.ImgHeight + glyph.glyphMatrix.bboxYmin) >> 6)), 1.1f); // coords.Add(r.Left); // coords.Add(r.Top); // coords.Add(r.Width); // coords.Add(r.Height); // //------------------------- // coords.Add(adjustX + c_x + left); // //coords.Add(baseline + ((int)((glyphData.ImgHeight + glyph.glyphMatrix.bboxYmin) * scale) >> 6)); // coords.Add(baseline + ((int)((glyphData.ImgHeight + glyphData.BBoxYMin) * scale) >> 6)); // //int w = (int)(glyph.glyphMatrix.advanceX * scale) >> 6; // int w = (int)(glyph.horiz_adv_x * scale) >> 6; // c_x += w; //} //_canvas.DrawSubImageWithMsdf(glBmp, coords.ToArray(), scale); //----------------------- //public override void DrawString(string text, double x, double y) //{ // char[] chars = text.ToCharArray(); // int j = chars.Length; // int buffsize = j * 2; // //get kerning list // TextureFont currentFont = this.CurrentFont as TextureFont; // SimpleFontAtlas fontAtlas = currentFont.FontAtlas; // ProperGlyph[] properGlyphs = new ProperGlyph[buffsize]; // currentFont.GetGlyphPos(chars, 0, buffsize, properGlyphs); // GLBitmap glBmp = currentFont.GLBmp; // if (glBmp == null) // { // //create glbmp // GlyphImage glyphImage = fontAtlas.TotalGlyph; // int[] buffer = glyphImage.GetImageBuffer(); // glBmp = new GLBitmap(glyphImage.Width, glyphImage.Height, buffer, false); // } // //int j = chars.Length; // // // float c_x = (float)x; // float c_y = (float)y; // //TODO: review here // //----------------- // //1. layout each glyph before render *** // float baseline = c_y - 24;//eg line height= 24 // //create a list // for (int i = 0; i < buffsize; ++i) // { // ProperGlyph glyph1 = properGlyphs[i]; // uint codepoint = properGlyphs[i].codepoint; // if (codepoint == 0) // { // break; // } // if (codepoint == 1173 && i > 1) // { // //check prev code point // codepoint = 1168; // } // TextureFontGlyphData glyphData; // if (!fontAtlas.GetRect((int)codepoint, out glyphData)) // { // //Rectangle r = glyphData.Rect; // //float x_min = glyphData.BBoxXMin / 64; // ////draw each glyph at specific position // ////_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); // //_canvas.DrawSubImageWithMsdf(glBmp, ref r, c_x + x_min, (float)(baseline + r.Height)); // ////c_x += r.Width - 10; // //c_x += (glyphData.AdvanceX / 64); // continue; // } // //------------------------------------------------------------- // //FontGlyph glyph = this.currentFont.GetGlyphByIndex(codepoint); // FontGlyph glyph = currentFont.GetGlyphByIndex(codepoint); // int left = (glyph.glyphMatrix.img_horiBearingX >> 6); // Rectangle r = glyphData.Rect; // int adjustX = 0; // int bboxYMin = glyph.glyphMatrix.bboxYmin >> 6; // if (bboxYMin > 1 || bboxYMin < -1) // { // // adjustX = 3; // } // //scale down 0.8; // _canvas.DrawSubImageWithMsdf(glBmp, ref r, adjustX + c_x + left, // (float)(baseline + ((int)(glyphData.ImgHeight + glyph.glyphMatrix.bboxYmin) >> 6)), 1.1f); // int w = (glyph.glyphMatrix.advanceX) >> 6; // c_x += (w); // } //} // public override void DrawString(string text, double x, double y) // { // ////in this version we draw string to image // ////and the write the image back to gl surface // //_winGfx.Clear(System.Drawing.Color.White); // //_winGfx.DrawString(text, _winFont, _winGfxBrush, 0, 0); // ////_winGfxBackBmp.Save("d:\\WImageTest\\a00123.png"); // //System.Drawing.SizeF textAreaSize = _winGfx.MeasureString(text, _winFont); // //var bmpData = _winGfxBackBmp.LockBits(new System.Drawing.Rectangle(0, 0, _winGfxBackBmp.Width, _winGfxBackBmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, _winGfxBackBmp.PixelFormat); // //int width = (int)textAreaSize.Width; // //int height = (int)textAreaSize.Height; // //ActualImage actualImg = new ActualImage(width, height, Agg.Image.PixelFormat.ARGB32); // ////------------------------------------------------------ // ////copy bmp from specific bmp area // ////and convert to GLBmp // //int stride = bmpData.Stride; // //byte[] buffer = actualImg.GetBuffer(); // //unsafe // //{ // // byte* header = (byte*)bmpData.Scan0; // // fixed (byte* dest0 = &buffer[0]) // // { // // byte* dest = dest0; // // byte* rowHead = header; // // int rowLen = width * 4; // // for (int h = 0; h < height; ++h) // // { // // header = rowHead; // // for (int n = 0; n < rowLen;) // // { // // //move next // // *(dest + 0) = *(header + 0); // // *(dest + 1) = *(header + 1); // // *(dest + 2) = *(header + 2); // // *(dest + 3) = *(header + 3); // // header += 4; // // dest += 4; // // n += 4; // // } // // //finish one row // // rowHead += stride; // // } // // } // //} // //_winGfxBackBmp.UnlockBits(bmpData); // ////------------------------------------------------------ // //GLBitmap glBmp = new GLBitmap(width, height, buffer, false); // //_canvas.DrawImageWithWhiteTransparent(glBmp, (float)x, (float)y); // //glBmp.Dispose(); // } }
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; //-------------------------- //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; for (int i = glyphPlanSeq.startAt; i < endBefore; ++i) { GlyphPlanList glyphPlanList = GlyphPlanSequence.UnsafeGetInteralGlyphPlanList(glyphPlanSeq); GlyphPlan glyph = glyphPlanList[i]; Typography.Rendering.TextureFontGlyphData glyphData; if (!_fontAtlas.TryGetGlyphDataByCodePoint(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); 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 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.DrawGlyphImageWithSubPixelRenderingTechnique(_glBmp, ref srcRect, g_x, g_y, scaleFromTexture); break; } } }