Browse Source

Fixed issues with SkiaSharp text layout and rendering.

pull/506/head
Jason Jarvis 11 years ago
parent
commit
2acc80df33
  1. 101
      src/Skia/Perspex.Skia/FormattedTextImpl.cs
  2. 14
      src/Skia/Perspex.Skia/SkiaSharpExtensions.cs
  3. 10
      src/iOS/Perspex.iOS/PerspexView.cs
  4. 25
      src/iOS/Perspex.iOSTestApplication/AppDelegate.cs
  5. 2
      src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj

101
src/Skia/Perspex.Skia/FormattedTextImpl.cs

@ -12,29 +12,18 @@ namespace Perspex.Skia
{ {
public SKPaint Paint { get; private set; } public SKPaint Paint { get; private set; }
//private readonly NativeFormattedText* _shared;
private readonly string _text;
public FormattedTextImpl(string text) public FormattedTextImpl(string text)
{ {
_text = text; _text = text;
Paint = new SKPaint(); Paint = new SKPaint();
//Length = length;
Paint.TextEncoding = SKTextEncoding.Utf16; Paint.TextEncoding = SKTextEncoding.Utf16;
Paint.IsStroke = false; Paint.IsStroke = false;
Paint.IsAntialias = true; Paint.IsAntialias = true;
LineOffset = 0; LineOffset = 0;
//Shared.WidthConstraint = -1.0;
//Data = new pchar[length + 1];
//memcpy(Data, data, length * 2);
//Data[length] = 0;
//Replace 0 characters with zero-width spaces (200B) //Replace 0 characters with zero-width spaces (200B)
_text = _text.Replace((char)0, (char) 0x200B); _text = _text.Replace((char)0, (char) 0x200B);
//Rebuild();
} }
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
@ -45,23 +34,13 @@ namespace Perspex.Skia
FormattedTextImpl instance = new FormattedTextImpl(text); FormattedTextImpl instance = new FormattedTextImpl(text);
instance.Paint.Typeface = typeface; instance.Paint.Typeface = typeface;
instance.Paint.TextSize = (float) fontSize; instance.Paint.TextSize = (float) fontSize;
instance.Paint.TextAlign = textAlignment.ToSKTextAlign();
//rv->Paint.setTextAlign(align); //TODO: Manually align
instance.Rebuild(); instance.Rebuild();
//NativeFormattedText* pShared;
//fixed (void* ptext = text)
//{
// IntPtr handle = MethodTable.Instance.CreateFormattedText(ptext, text.Length,
// TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight),
// (float) fontSize, textAlignment, &pShared);
// return new FormattedTextImpl(handle, pShared, text);
//}
return instance; return instance;
} }
private readonly string _text;
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>(); readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
readonly List<Rect> _rects = new List<Rect>(); readonly List<Rect> _rects = new List<Rect>();
@ -129,8 +108,16 @@ namespace Perspex.Skia
public void SetForegroundBrush(IBrush brush, int startIndex, int length) public void SetForegroundBrush(IBrush brush, int startIndex, int length)
{ {
// Is this method even relevant now???
//
//var scb = brush as SolidColorBrush;
//if (scb != null)
//{
// Layout.Attributes.Insert(brushAttr);
//}
} }
void Rebuild() void Rebuild()
{ {
var length = _text.Length; var length = _text.Length;
@ -141,12 +128,18 @@ namespace Perspex.Skia
_skiaLines = new List<PerspexFormattedTextLine>(); _skiaLines = new List<PerspexFormattedTextLine>();
int curOff = 0; int curOff = 0;
int curY = 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;
// TODO: cannot find Font Metrics in SkiaSharp! // Rendering is relative to baseline
//SKPaint.FontMetrics metrics; LineOffset = -metrics.Top;
float lineHeight = Paint.TextSize; // Paint.getFontMetrics(&metrics);
LineOffset = 0; // -metrics.fTop;
string subString; string subString;
@ -171,18 +164,19 @@ namespace Perspex.Skia
subString = _text.Substring(curOff); subString = _text.Substring(curOff);
// This method is not linking into SkiaSharp so we must use the RAW buffer version // TODO: This method is not linking into SkiaSharp so we must use the RAW buffer version
//measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2; //measured = (int)Paint.BreakText(subString, constraint, out lineWidth) / 2;
bytes = Encoding.ASCII.GetBytes(subString); bytes = Encoding.UTF8.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned); pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject(); pointer = pinnedArray.AddrOfPinnedObject();
measured = (int)Paint.BreakText(pointer, (IntPtr)bytes.Length, constraint, out lineWidth); // for some reason I have to pass nBytes * 2
measured = (int)Paint.BreakText(pointer, (IntPtr)(bytes.Length * 2), constraint, out lineWidth) / 2;
// some weird unicode byte issue again // some weird unicode byte issue again
if(subString.Length % 2 == 1) //if(subString.Length % 2 == 1)
{ //{
measured -= 1; // measured -= 1;
} //}
pinnedArray.Free(); pinnedArray.Free();
@ -232,15 +226,7 @@ namespace Perspex.Skia
prevRight = _skiaRects[i - 1].Right; prevRight = _skiaRects[i - 1].Right;
subString = _text.Substring(line.Start, i - line.Start + 1); subString = _text.Substring(line.Start, i - line.Start + 1);
float w = Paint.MeasureText(subString);
// Unfortunately this version appears to be incorrect and skipping
// even characters. Some issue with Unicode?
//float w = Paint.MeasureText(subString);
bytes = Encoding.ASCII.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
float w = Paint.MeasureText(pointer, (IntPtr)bytes.Length);
pinnedArray.Free();
SKRect rc; SKRect rc;
rc.Left = prevRight; rc.Left = prevRight;
@ -251,25 +237,14 @@ namespace Perspex.Skia
} }
subString = _text.Substring(line.Start, line.Length); subString = _text.Substring(line.Start, line.Length);
line.Width = Paint.MeasureText(subString);
// Unfortunately this version appears to be incorrect and skipping
// even characters. Some issue with Unicode?
//line.Width = Paint.MeasureText(subString);
bytes = Encoding.ASCII.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
pointer = pinnedArray.AddrOfPinnedObject();
line.Width = Paint.MeasureText(pointer, (IntPtr)bytes.Length);
pinnedArray.Free();
_skiaLines.Add(line); _skiaLines.Add(line);
curY += (int)lineHeight; curY += lineHeight; // + mLeading;
curOff += measured + extraSkip; curOff += measured + extraSkip;
} }
//Shared.Lines = Lines.data();
//Shared.CharRects = Rects.data();
// Now convert to Perspex data formats // Now convert to Perspex data formats
_lines.Clear(); _lines.Clear();
_rects.Clear(); _rects.Clear();
@ -296,12 +271,11 @@ namespace Perspex.Skia
} }
} }
internal void Draw(SKCanvas canvas, /*PerspexBrush* foreground, */ SKPoint origin) internal void Draw(SKCanvas canvas, SKPoint origin)
{ {
SKPaint paint = Paint; SKPaint paint = Paint;
//ConfigurePaint(paint, ctx, foreground);
/* /* This originated from Native code, it might be useful
//Debugging code for character positions //Debugging code for character positions
SkPaint dpaint; SkPaint dpaint;
ctx->Canvas->save(); ctx->Canvas->save();
@ -325,11 +299,14 @@ namespace Perspex.Skia
ctx->Canvas->restore(); ctx->Canvas->restore();
*/ */
// These seems to vertically align the text properly
var yOffset = (LineOffset); // + 1) / 2;
for (int c = 0; c < _skiaLines.Count; c++) for (int c = 0; c < _skiaLines.Count; c++)
{ {
PerspexFormattedTextLine line = _skiaLines[c]; PerspexFormattedTextLine line = _skiaLines[c];
var subString = _text.Substring(line.Start, line.Length); var subString = _text.Substring(line.Start, line.Length);
canvas.DrawText(subString, origin.X, origin.Y + line.Top + LineOffset, paint); canvas.DrawText(subString, origin.X, origin.Y + line.Top + yOffset, paint);
} }
} }

14
src/Skia/Perspex.Skia/SkiaSharpExtensions.cs

@ -1,4 +1,5 @@
using SkiaSharp; using Perspex.Media;
using SkiaSharp;
namespace Perspex.Skia namespace Perspex.Skia
@ -53,5 +54,16 @@ namespace Perspex.Skia
case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat; case Media.GradientSpreadMethod.Repeat: return SKShaderTileMode.Repeat;
} }
} }
public static SKTextAlign ToSKTextAlign(this TextAlignment a)
{
switch (a)
{
default:
case TextAlignment.Left: return SKTextAlign.Left;
case TextAlignment.Center: return SKTextAlign.Center;
case TextAlignment.Right: return SKTextAlign.Right;
}
}
} }
} }

10
src/iOS/Perspex.iOS/PerspexView.cs

@ -75,7 +75,15 @@ namespace Perspex.iOS
public IPlatformHandle Handle => PerspexPlatformHandle; public IPlatformHandle Handle => PerspexPlatformHandle;
public double Scaling => 1; public double Scaling
{
get
{
// This does not appear to make any difference, but on iOS we
// have Retina (x2) and we probably want this eventually
return 1; //UIScreen.MainScreen.Scale;
}
}
public WindowState WindowState public WindowState WindowState
{ {

25
src/iOS/Perspex.iOSTestApplication/AppDelegate.cs

@ -33,7 +33,8 @@ namespace Perspex.iOSTestApplication
MainWindow.RootNamespace = "Perspex.iOSTestApplication"; MainWindow.RootNamespace = "Perspex.iOSTestApplication";
var window = MainWindow.Create(); var window = MainWindow.Create();
window.Show(); //var window = Create();
window.Show();
app.Run(window); app.Run(window);
return true; return true;
@ -48,18 +49,30 @@ namespace Perspex.iOSTestApplication
//Width = 900, //Width = 900,
//Height = 480, //Height = 480,
Background = Brushes.Red, Background = Brushes.Red,
Content = new Grid Content = new StackPanel
{ {
Margin = new Thickness(100), Margin = new Thickness(30),
Background = Brushes.Yellow, Background = Brushes.Yellow,
Children = new Controls.Controls Children = new Controls.Controls
{ {
new TextBlock
{
Text = "TEXT BLOCK",
Width = 300,
Height = 40,
Background = Brushes.White,
Foreground = Brushes.Black
},
new Button new Button
{ {
Content = "Hello World!", Content = "BUTTON",
Width = 200, Width = 150,
Height = 200 Height = 40,
Background = Brushes.LightGreen,
Foreground = Brushes.Black
} }
} }
} }
}; };

2
src/iOS/Perspex.iOSTestApplication/Perspex.iOSTestApplication.csproj

@ -47,7 +47,7 @@
<CodesignKey>iPhone Developer</CodesignKey> <CodesignKey>iPhone Developer</CodesignKey>
<MtouchDebug>True</MtouchDebug> <MtouchDebug>True</MtouchDebug>
<MtouchSdkVersion>9.1</MtouchSdkVersion> <MtouchSdkVersion>9.1</MtouchSdkVersion>
<MtouchLink>SdkOnly</MtouchLink> <MtouchLink>None</MtouchLink>
<MtouchProfiling>False</MtouchProfiling> <MtouchProfiling>False</MtouchProfiling>
<MtouchFastDev>False</MtouchFastDev> <MtouchFastDev>False</MtouchFastDev>
<MtouchUseLlvm>False</MtouchUseLlvm> <MtouchUseLlvm>False</MtouchUseLlvm>

Loading…
Cancel
Save