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; }
//private readonly NativeFormattedText* _shared;
private readonly string _text;
public FormattedTextImpl(string text)
{
_text = text;
Paint = new SKPaint();
//Length = length;
Paint.TextEncoding = SKTextEncoding.Utf16;
Paint.IsStroke = false;
Paint.IsAntialias = true;
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)
_text = _text.Replace((char)0, (char) 0x200B);
//Rebuild();
}
public static FormattedTextImpl Create(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
@ -45,23 +34,13 @@ namespace Perspex.Skia
FormattedTextImpl instance = new FormattedTextImpl(text);
instance.Paint.Typeface = typeface;
instance.Paint.TextSize = (float) fontSize;
//rv->Paint.setTextAlign(align); //TODO: Manually align
instance.Paint.TextAlign = textAlignment.ToSKTextAlign();
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;
}
private readonly string _text;
readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
readonly List<Rect> _rects = new List<Rect>();
@ -129,8 +108,16 @@ namespace Perspex.Skia
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()
{
var length = _text.Length;
@ -141,12 +128,18 @@ namespace Perspex.Skia
_skiaLines = new List<PerspexFormattedTextLine>();
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!
//SKPaint.FontMetrics metrics;
float lineHeight = Paint.TextSize; // Paint.getFontMetrics(&metrics);
LineOffset = 0; // -metrics.fTop;
// Rendering is relative to baseline
LineOffset = -metrics.Top;
string subString;
@ -171,18 +164,19 @@ namespace Perspex.Skia
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;
bytes = Encoding.ASCII.GetBytes(subString);
bytes = Encoding.UTF8.GetBytes(subString);
pinnedArray = GCHandle.Alloc(bytes, GCHandleType.Pinned);
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
if(subString.Length % 2 == 1)
{
measured -= 1;
}
//if(subString.Length % 2 == 1)
//{
// measured -= 1;
//}
pinnedArray.Free();
@ -232,15 +226,7 @@ namespace Perspex.Skia
prevRight = _skiaRects[i - 1].Right;
subString = _text.Substring(line.Start, i - line.Start + 1);
// 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();
float w = Paint.MeasureText(subString);
SKRect rc;
rc.Left = prevRight;
@ -251,25 +237,14 @@ namespace Perspex.Skia
}
subString = _text.Substring(line.Start, line.Length);
// 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();
line.Width = Paint.MeasureText(subString);
_skiaLines.Add(line);
curY += (int)lineHeight;
curY += lineHeight; // + mLeading;
curOff += measured + extraSkip;
}
//Shared.Lines = Lines.data();
//Shared.CharRects = Rects.data();
// Now convert to Perspex data formats
_lines.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;
//ConfigurePaint(paint, ctx, foreground);
/*
/* This originated from Native code, it might be useful
//Debugging code for character positions
SkPaint dpaint;
ctx->Canvas->save();
@ -325,11 +299,14 @@ namespace Perspex.Skia
ctx->Canvas->restore();
*/
// These seems to vertically align the text properly
var yOffset = (LineOffset); // + 1) / 2;
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);
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
@ -53,5 +54,16 @@ namespace Perspex.Skia
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 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
{

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

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

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

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

Loading…
Cancel
Save