internal void Draw(SKCanvas canvas, SKPoint origin) { SKPaint paint = Paint; /* TODO: This originated from Native code, it might be useful for debugging character positions as * we improve the FormattedText support. Will need to port this to C# obviously. Rmove when * not needed anymore. * * SkPaint dpaint; * ctx->Canvas->save(); * ctx->Canvas->translate(origin.fX, origin.fY); * for (int c = 0; c < Lines.size(); c++) * { * dpaint.setARGB(255, 0, 0, 0); * SkRect rc; * rc.fLeft = 0; * rc.fTop = Lines[c].Top; * rc.fRight = Lines[c].Width; * rc.fBottom = rc.fTop + LineOffset; * ctx->Canvas->drawRect(rc, dpaint); * } * for (int c = 0; c < Length; c++) * { * dpaint.setARGB(255, c % 10 * 125 / 10 + 125, (c * 7) % 10 * 250 / 10, (c * 13) % 10 * 250 / 10); * dpaint.setStyle(SkPaint::kFill_Style); * ctx->Canvas->drawRect(Rects[c], dpaint); * } * ctx->Canvas->restore(); */ for (int c = 0; c < _skiaLines.Count; c++) { PerspexFormattedTextLine line = _skiaLines[c]; var subString = _text.Substring(line.Start, line.Length); canvas.DrawText(subString, origin.X, origin.Y + line.Top + LineOffset, paint); } }
void Rebuild() { var length = _text.Length; _lines.Clear(); _skiaRects = new SKRect[length]; _skiaLines = new List <PerspexFormattedTextLine>(); int curOff = 0; float curY = 0; var metrics = Paint.FontMetrics; var mTop = metrics.Top; // The greatest distance above the baseline for any glyph (will be <= 0). var mBottom = metrics.Bottom; // The greatest distance below the baseline for any glyph (will be >= 0). var mLeading = metrics.Leading; // The recommended distance to add between lines of text (will be >= 0). // This seems like the best measure of full vertical extent float lineHeight = mBottom - mTop; // Rendering is relative to baseline LineOffset = -metrics.Top; string subString; byte[] bytes; GCHandle pinnedArray; IntPtr pointer; for (int c = 0; curOff < length; c++) { float lineWidth = -1; int measured; int extraSkip = 0; if (WidthConstraint <= 0) { measured = length; } else { float constraint = WidthConstraint; if (constraint > MAX_LINE_WIDTH) { constraint = MAX_LINE_WIDTH; } subString = _text.Substring(curOff); // TODO: This method is not linking into SkiaSharp so we must use the RAW buffer version for now //measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2; bytes = Encoding.UTF8.GetBytes(subString); pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned); pointer = pinnedArray.AddrOfPinnedObject(); // for some reason I have to pass nBytes * 2. I assume under the hood it expects Unicode/WChar?? measured = (int)Paint.BreakText(pointer, (IntPtr)(bytes.Length * 2), constraint, out lineWidth) / 2; pinnedArray.Free(); if (measured == 0) { measured = 1; lineWidth = -1; } char nextChar = ' '; if (curOff + measured < length) { nextChar = _text[curOff + measured]; } if (nextChar != ' ') { // Perform scan for the last space and end the line there for (int si = curOff + measured - 1; si > curOff; si--) { if (_text[si] == ' ') { measured = si - curOff; extraSkip = 1; break; } } } } PerspexFormattedTextLine line = new PerspexFormattedTextLine(); line.Start = curOff; line.Length = measured; line.Width = lineWidth; line.Height = lineHeight; line.Top = curY; if (line.Width < 0) { line.Width = _skiaRects[line.Start + line.Length - 1].Right; } // Build character rects for (int i = line.Start; i < line.Start + line.Length; i++) { float prevRight = 0; if (i != line.Start) { prevRight = _skiaRects[i - 1].Right; } subString = _text.Substring(line.Start, i - line.Start + 1); float w = Paint.MeasureText(subString); SKRect rc; rc.Left = prevRight; rc.Right = w; rc.Top = line.Top; rc.Bottom = line.Top + line.Height; _skiaRects[i] = rc; } subString = _text.Substring(line.Start, line.Length); line.Width = Paint.MeasureText(subString); _skiaLines.Add(line); curY += lineHeight; // TODO: We may want to consider adding Leading to the vertical line spacing but for now // it appears to make no difference. Revisit as part of FormattedText improvements. // //curY += mLeading; curOff += measured + extraSkip; } // Now convert to Perspex data formats _lines.Clear(); _rects.Clear(); float maxX = 0; for (var c = 0; c < _skiaLines.Count; c++) { var w = _skiaLines[c].Width; if (maxX < w) { maxX = w; } _lines.Add(new FormattedTextLine(_skiaLines[c].Length, _skiaLines[c].Height)); } for (var c = 0; c < _text.Length; c++) { _rects.Add(_skiaRects[c].ToPerspexRect()); } if (_skiaLines.Count == 0) { _size = new Size(); } else { var lastLine = _skiaLines[_skiaLines.Count - 1]; _size = new Size(maxX, lastLine.Top + lastLine.Height); } }