/// <summary> /// Obtains bounding rectangle. /// </summary> /// <param name="range"></param> /// <param name="min"></param> /// <param name="max"></param> public void GetBoundingRect(Vector2i range, out Vector2f min, out Vector2f max) { ValidateRange(range); min = new Vector2f(float.PositiveInfinity, float.PositiveInfinity); max = new Vector2f(float.NegativeInfinity, float.NegativeInfinity); for (int i = range.X; i <= range.Y; i++) { RenderGlyphInfo info = glyphs[i]; Vector2f lb = info.LeftBottom, rt = lb + info.MultiSize; if (lb.X < min.X) { min.X = lb.X; } if (lb.Y < min.Y) { min.Y = lb.Y; } if (rt.X > max.X) { max.X = rt.X; } if (rt.Y > max.Y) { max.Y = rt.Y; } } }
/// <summary> /// Constructs render info from lines. /// </summary> /// <param name="lines"></param> public TextRenderInfo(ICanvas canvas, Font font, float fontSize, params TextRenderLineInfo[] lines) { this.lines = lines; // We extract glyphs. List <RenderGlyphInfo> t = new List <RenderGlyphInfo>(); for (int i = 0; i < lines.Length; i++) { TextRenderLineInfo info = lines[i]; for (uint j = 0; j < info.GlyphCount; j++) { RenderGlyphInfo g = info[j]; t.Add(info[j]); } } this.glyphs = t.ToArray(); // May adjust. this.fullRange = new Vector2i(0, glyphs.Length - 1); this.canvas = canvas; this.font = font; this.fontSize = fontSize; }
/// <summary> /// Translates text render information by vector. /// </summary> /// <param name="translateVector"></param> public void Translate(Vector2f translateVector) { for (int i = 0; i < glyphs.Length; i++) { RenderGlyphInfo info = glyphs[i]; info.Adjust(translateVector); } // Must adjust line spans. for (int i = 0; i < lines.Length; i++) { lines[i].AdjustSpan(translateVector.X); } }
/// <summary> /// Render outlined characters. /// </summary> /// <param name="pen"></param> /// <param name="range"></param> public void Render(Pen pen, Vector2i range) { if (range.Y < range.X) { return; } ValidateRange(range); canvas.Begin(CanvasRenderFlags.SoftwarePositionTransform); try { for (int i = range.X; i <= range.Y; i++) { RenderGlyphInfo info = glyphs[i]; if (glyphs[i].RenderingData == null) { continue; } // We set transform. canvas.Transform = new LinearTransform( Matrix4x4f.CreateTranslate(info.LeftBottom.Vec3) * Matrix4x4f.CreateScale(new Vector3f(fontSize, fontSize, fontSize)) ); // We finally render. TriangleSoup2f soup = info.RenderingData.AcquireOutline(canvas.CanvasInfo.TesselationResolution, pen.ToOutlineTesselationOptions(canvas.CanvasInfo)); canvas.FillShape(pen.Fill, soup, info.AttachedMapper); } } finally { canvas.End(); } }
/// <summary> /// Prepares text for rendering, with rectange where data should be rendered. /// </summary> public TextRenderInfo Prepare(ICanvas canvas, string text, float fontSize, TextOptions options, float lineSpacing, Vector2f leftBottom, Vector2f size) { // We extract information and convert them. bool newLineOrdinary = HasFlag(options, TextOptions.NewLineOrdinary); bool groupWords = HasFlag(options, TextOptions.NotGroupWords); bool allowMulti = HasFlag(options, TextOptions.AllowMulticharacters); if (!HasFlag(options, TextOptions.UseCanvasUnits)) { // We use Y dimension, since it is primary axis for text (we want it properly aligned). fontSize *= canvas.CanvasInfo.CanvasUnitSize.Y / canvas.CanvasInfo.CanvasPixelSize.Y; } lineSpacing *= fontSize; // We go for each character. float lineY = leftBottom.Y + size.Y - fontSize; float lineX = leftBottom.X; // Return data. List <TextRenderLineInfo> lines = new List <TextRenderLineInfo>(); TextRenderLineInfo currentLine = new TextRenderLineInfo( new Vector2f(leftBottom.X, leftBottom.X + size.X)); for (int i = 0; i < text.Length; i++) { // We first check for out of range. if (lineY < leftBottom.Y) { break; } // We select next character. int advIndex = 1; Glyph obj = null; // We find next glyph. if (text[i] != '\n' || newLineOrdinary) { obj = SelectGlyph(text, i, out advIndex, allowMulti); } // We have it multiplied. float adv = obj.Advance; adv *= fontSize; // We check for line fit. if ((!newLineOrdinary && text[i] == '\n') || (lineX + adv >= leftBottom.X + size.X)) { // We go to new line. lines.Add(currentLine); lineY -= lineSpacing; lineX = leftBottom.X; if (lineY < leftBottom.Y) { currentLine = null; break; } else { i--; currentLine = new TextRenderLineInfo( new Vector2f(leftBottom.X, leftBottom.X + size.X)); continue; } } // We advance and add it. We must carefully handle multi-char glyphs. for (int j = 0; j < advIndex; j++) { // We create information. RenderGlyphInfo info2 = new RenderGlyphInfo( text[i + j], j == 0 ? text.Substring(i, advIndex) : string.Empty, new Vector2f(lineX + j * (adv / advIndex), lineY), new Vector2f(adv / advIndex, fontSize), j == 0 ? new Vector2f(adv, fontSize) : Vector2f.Zero, j == 0 ? obj : null, i + j); currentLine.Append(info2); } // We advance. lineX += adv; } if (currentLine != null) { lines.Add(currentLine); } TextRenderInfo info = new TextRenderInfo(canvas, this, fontSize, lines.ToArray()); // We now post-transform using flags. if (HasFlag(options, TextOptions.Top)) { // Already positioned. } else if (HasFlag(options, TextOptions.Bottom)) { float disp = lines[lines.Count - 1].Bottom - leftBottom.Y; info.Translate(new Vector2f(0, -disp)); } else { // Center positioning. float disp = (lines[lines.Count - 1].Bottom - leftBottom.Y) / 2.0f; info.Translate(new Vector2f(0, -disp)); } // We also align. if (HasFlag(options, TextOptions.Left)) { // Already aligned. //info.LeftAlignLines(); } else if (HasFlag(options, TextOptions.Right)) { info.RightAlignLines(); } else { info.CenterAlignLines(); } return(info); }
/// <summary> /// Appends a glyph. /// </summary> /// <param name="info"></param> public void Append(RenderGlyphInfo info) { glyphs.Add(info); }