Browse Source

Merge branch 'master' into control-validation-message-fix

pull/4704/head
Kieran Devlin 6 years ago
committed by GitHub
parent
commit
7ffdcdecee
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      nukebuild/Build.cs
  2. 3
      samples/RenderDemo/Pages/GlyphRunPage.xaml.cs
  3. 4
      src/Avalonia.Controls/Primitives/AccessText.cs
  4. 5
      src/Avalonia.Controls/TextBlock.cs
  5. 2
      src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs
  6. 8
      src/Avalonia.Native/Avalonia.Native.csproj
  7. 20
      src/Avalonia.Visuals/ApiCompatBaseline.txt
  8. 5
      src/Avalonia.Visuals/Media/DrawingContext.cs
  9. 49
      src/Avalonia.Visuals/Media/GlyphRun.cs
  10. 13
      src/Avalonia.Visuals/Media/GlyphRunDrawing.cs
  11. 17
      src/Avalonia.Visuals/Media/TextDecoration.cs
  12. 7
      src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs
  13. 11
      src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs
  14. 6
      src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs
  15. 10
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  16. 3
      src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs
  17. 29
      src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs
  18. 2
      src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs
  19. 3
      src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs
  20. 4
      src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs
  21. 13
      src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs
  22. 8
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  23. 7
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  24. 2
      tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

14
nukebuild/Build.cs

@ -138,9 +138,19 @@ partial class Build : NukeBuild
.SetWorkingDirectory(webappDir) .SetWorkingDirectory(webappDir)
.SetCommand("dist")); .SetCommand("dist"));
}); });
Target Compile => _ => _ Target CompileNative => _ => _
.DependsOn(Clean) .DependsOn(Clean)
.OnlyWhenStatic(() => EnvironmentInfo.IsOsx)
.Executes(() =>
{
var project = $"{RootDirectory}/native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/";
var args = $"-project {project} -configuration {Parameters.Configuration} CONFIGURATION_BUILD_DIR={RootDirectory}/Build/Products/Release";
ProcessTasks.StartProcess("xcodebuild", args).AssertZeroExitCode();
});
Target Compile => _ => _
.DependsOn(Clean, CompileNative)
.DependsOn(CompileHtmlPreviewer) .DependsOn(CompileHtmlPreviewer)
.Executes(async () => .Executes(async () =>
{ {

3
samples/RenderDemo/Pages/GlyphRunPage.xaml.cs

@ -61,7 +61,6 @@ namespace RenderDemo.Pages
{ {
Foreground = Brushes.Black, Foreground = Brushes.Black,
GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices), GlyphRun = new GlyphRun(_glyphTypeface, _fontSize, _glyphIndices),
BaselineOrigin = new Point(0, -_glyphTypeface.Ascent * scale)
}; };
drawingGroup.Children.Add(glyphRunDrawing); drawingGroup.Children.Add(glyphRunDrawing);
@ -69,7 +68,7 @@ namespace RenderDemo.Pages
var geometryDrawing = new GeometryDrawing var geometryDrawing = new GeometryDrawing
{ {
Pen = new Pen(Brushes.Black), Pen = new Pen(Brushes.Black),
Geometry = new RectangleGeometry { Rect = glyphRunDrawing.GlyphRun.Bounds } Geometry = new RectangleGeometry { Rect = new Rect(glyphRunDrawing.GlyphRun.Size) }
}; };
drawingGroup.Children.Add(geometryDrawing); drawingGroup.Children.Add(geometryDrawing);

4
src/Avalonia.Controls/Primitives/AccessText.cs

@ -126,7 +126,7 @@ namespace Avalonia.Controls.Primitives
if (shapedTextCharacters.GlyphRun.Characters.End < textPosition) if (shapedTextCharacters.GlyphRun.Characters.End < textPosition)
{ {
currentX += shapedTextCharacters.GlyphRun.Bounds.Width; currentX += shapedTextCharacters.Size.Width;
continue; continue;
} }
@ -143,7 +143,7 @@ namespace Avalonia.Controls.Primitives
width = 0.0; width = 0.0;
} }
return new Rect(currentX, currentY, width, shapedTextCharacters.GlyphRun.Bounds.Height); return new Rect(currentX, currentY, width, shapedTextCharacters.Size.Height);
} }
} }

5
src/Avalonia.Controls/TextBlock.cs

@ -434,7 +434,10 @@ namespace Avalonia.Controls
var padding = Padding; var padding = Padding;
TextLayout.Draw(context, new Point(padding.Left + offsetX, padding.Top)); using (context.PushPostTransform(Matrix.CreateTranslation(padding.Left + offsetX, padding.Top)))
{
TextLayout.Draw(context);
}
} }
/// <summary> /// <summary>

2
src/Avalonia.Headless/HeadlessPlatformRenderInterface.cs

@ -385,7 +385,7 @@ namespace Avalonia.Headless
} }
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
} }

8
src/Avalonia.Native/Avalonia.Native.csproj

@ -1,7 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<IsPackable>false</IsPackable> <PackAvaloniaNative Condition="'$(PackAvaloniaNative)' == ''">$([MSBuild]::IsOSPlatform(OSX))</PackAvaloniaNative>
<IsPackable>$(PackAvaloniaNative)</IsPackable>
<IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable> <IsPackable Condition="'$([MSBuild]::IsOSPlatform(OSX))' == 'True'">true</IsPackable>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.0</TargetFramework>
<CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath> <CastXmlPath Condition="Exists('/usr/bin/castxml')">/usr/bin/castxml</CastXmlPath>
@ -10,8 +11,9 @@
<SharpGenGenerateConsumerBindMapping>false</SharpGenGenerateConsumerBindMapping> <SharpGenGenerateConsumerBindMapping>false</SharpGenGenerateConsumerBindMapping>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release' AND '$([MSBuild]::IsOSPlatform(OSX))' == 'true'"> <ItemGroup Condition="'$(PackAvaloniaNative)' == 'true'">
<Content Include="../../Build/Products/Release/libAvalonia.Native.OSX.dylib"> <Content Include="../../Build/Products/Release/libAvalonia.Native.OSX.dylib">
<Link>libAvaloniaNative.dylib</Link>
<PackagePath>runtimes/osx/native/libAvaloniaNative.dylib</PackagePath> <PackagePath>runtimes/osx/native/libAvaloniaNative.dylib</PackagePath>
<Pack>true</Pack> <Pack>true</Pack>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -26,4 +28,4 @@
<ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" /> <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" /> <ProjectReference Include="..\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

20
src/Avalonia.Visuals/ApiCompatBaseline.txt

@ -1,15 +1,33 @@
Compat issues with assembly Avalonia.Visuals: Compat issues with assembly Avalonia.Visuals:
MembersMustExist : Member 'public void Avalonia.Media.DrawingContext.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.GetOrAddTypeface(Avalonia.Media.FontFamily, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.Typeface Avalonia.Media.FontManager.MatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.GlyphRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.StyledProperty<Avalonia.Point> Avalonia.StyledProperty<Avalonia.Point> Avalonia.Media.GlyphRunDrawing.BaselineOriginProperty' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Point Avalonia.Media.GlyphRunDrawing.BaselineOrigin.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.GlyphRunDrawing.BaselineOrigin.set(Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract. CannotSealType : Type 'Avalonia.Media.Typeface' is actually (has the sealed modifier) sealed in the implementation but not sealed in the contract.
TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract. TypeCannotChangeClassification : Type 'Avalonia.Media.Typeface' is a 'struct' in the implementation but is a 'class' in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract. CannotMakeMemberNonVirtual : Member 'public System.Boolean Avalonia.Media.Typeface.Equals(System.Object)' is non-virtual in the implementation but is virtual in the contract.
CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract. CannotMakeMemberNonVirtual : Member 'public System.Int32 Avalonia.Media.Typeface.GetHashCode()' is non-virtual in the implementation but is virtual in the contract.
TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'Avalonia.Media.Fonts.FontKey' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.DrawableTextRun.Bounds.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.DrawableTextRun.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Size Avalonia.Media.TextFormatting.DrawableTextRun.Size.get()' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public Avalonia.Rect Avalonia.Media.TextFormatting.ShapedTextCharacters.Bounds.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.ShapedTextCharacters.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLayout.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak' is abstract in the implementation but is missing in the contract.
CannotAddAbstractMembers : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext)' is abstract in the implementation but is missing in the contract.
MembersMustExist : Member 'public void Avalonia.Media.TextFormatting.TextLine.Draw(Avalonia.Media.DrawingContext, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.LineBreak.get()' does not exist in the implementation but it does exist in the contract.
CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract. CannotAddAbstractMembers : Member 'public Avalonia.Media.TextFormatting.TextLineBreak Avalonia.Media.TextFormatting.TextLine.TextLineBreak.get()' is abstract in the implementation but is missing in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun)' is present in the implementation but not in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public void Avalonia.Platform.IDrawingContextImpl.DrawGlyphRun(Avalonia.Media.IBrush, Avalonia.Media.GlyphRun, Avalonia.Point)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' is present in the contract but not in the implementation.
MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract. MembersMustExist : Member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Fonts.FontKey)' does not exist in the implementation but it does exist in the contract.
InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract. InterfacesShouldHaveSameMembers : Interface member 'public System.Boolean Avalonia.Platform.IFontManagerImpl.TryMatchCharacter(System.Int32, Avalonia.Media.FontStyle, Avalonia.Media.FontWeight, Avalonia.Media.FontFamily, System.Globalization.CultureInfo, Avalonia.Media.Typeface)' is present in the implementation but not in the contract.
Total Issues: 13 Total Issues: 31

5
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -206,14 +206,13 @@ namespace Avalonia.Media
/// </summary> /// </summary>
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="glyphRun">The glyph run.</param> /// <param name="glyphRun">The glyph run.</param>
/// <param name="baselineOrigin">The baseline origin of the glyph run.</param> public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin)
{ {
Contract.Requires<ArgumentNullException>(glyphRun != null); Contract.Requires<ArgumentNullException>(glyphRun != null);
if (foreground != null) if (foreground != null)
{ {
PlatformImpl.DrawGlyphRun(foreground, glyphRun, baselineOrigin); PlatformImpl.DrawGlyphRun(foreground, glyphRun);
} }
} }

49
src/Avalonia.Visuals/Media/GlyphRun.cs

@ -16,8 +16,9 @@ namespace Avalonia.Media
private IGlyphRunImpl _glyphRunImpl; private IGlyphRunImpl _glyphRunImpl;
private GlyphTypeface _glyphTypeface; private GlyphTypeface _glyphTypeface;
private double _fontRenderingEmSize; private double _fontRenderingEmSize;
private Rect? _bounds; private Size? _size;
private int _biDiLevel; private int _biDiLevel;
private Point? _baselineOrigin;
private ReadOnlySlice<ushort> _glyphIndices; private ReadOnlySlice<ushort> _glyphIndices;
private ReadOnlySlice<double> _glyphAdvances; private ReadOnlySlice<double> _glyphAdvances;
@ -89,6 +90,20 @@ namespace Avalonia.Media
set => Set(ref _fontRenderingEmSize, value); set => Set(ref _fontRenderingEmSize, value);
} }
/// <summary>
/// Gets or sets the baseline origin of the<see cref="GlyphRun"/>.
/// </summary>
public Point BaselineOrigin
{
get
{
_baselineOrigin ??= CalculateBaselineOrigin();
return _baselineOrigin.Value;
}
set => Set(ref _baselineOrigin, value);
}
/// <summary> /// <summary>
/// Gets or sets an array of <see cref="ushort"/> values that represent the glyph indices in the rendering physical font. /// Gets or sets an array of <see cref="ushort"/> values that represent the glyph indices in the rendering physical font.
/// </summary> /// </summary>
@ -156,16 +171,13 @@ namespace Avalonia.Media
/// <summary> /// <summary>
/// Gets or sets the conservative bounding box of the <see cref="GlyphRun"/>. /// Gets or sets the conservative bounding box of the <see cref="GlyphRun"/>.
/// </summary> /// </summary>
public Rect Bounds public Size Size
{ {
get get
{ {
if (_bounds == null) _size ??= CalculateSize();
{
_bounds = CalculateBounds();
}
return _bounds.Value; return _size.Value;
} }
} }
@ -200,7 +212,7 @@ namespace Avalonia.Media
if (characterHit.FirstCharacterIndex + characterHit.TrailingLength > Characters.End) if (characterHit.FirstCharacterIndex + characterHit.TrailingLength > Characters.End)
{ {
return Bounds.Width; return Size.Width;
} }
var glyphIndex = FindGlyphIndex(characterHit.FirstCharacterIndex); var glyphIndex = FindGlyphIndex(characterHit.FirstCharacterIndex);
@ -257,7 +269,7 @@ namespace Avalonia.Media
} }
//After //After
if (distance > Bounds.Size.Width) if (distance > Size.Width)
{ {
isInside = false; isInside = false;
@ -529,12 +541,21 @@ namespace Avalonia.Media
} }
/// <summary> /// <summary>
/// Calculates the bounds of the <see cref="GlyphRun"/>. /// Calculates the default baseline origin of the <see cref="GlyphRun"/>.
/// </summary>
/// <returns>The baseline origin.</returns>
private Point CalculateBaselineOrigin()
{
return new Point(0, -GlyphTypeface.Ascent * Scale);
}
/// <summary>
/// Calculates the size of the <see cref="GlyphRun"/>.
/// </summary> /// </summary>
/// <returns> /// <returns>
/// The calculated bounds. /// The calculated bounds.
/// </returns> /// </returns>
private Rect CalculateBounds() private Size CalculateSize()
{ {
var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
@ -555,7 +576,7 @@ namespace Avalonia.Media
} }
} }
return new Rect(0, GlyphTypeface.Ascent * Scale, width, height); return new Size(width, height);
} }
private void Set<T>(ref T field, T value) private void Set<T>(ref T field, T value)
@ -590,11 +611,15 @@ namespace Avalonia.Media
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
_baselineOrigin = new Point(0, -GlyphTypeface.Ascent * Scale);
var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>(); var platformRenderInterface = AvaloniaLocator.Current.GetService<IPlatformRenderInterface>();
_glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width); _glyphRunImpl = platformRenderInterface.CreateGlyphRun(this, out var width);
var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale; var height = (GlyphTypeface.Descent - GlyphTypeface.Ascent + GlyphTypeface.LineGap) * Scale;
_size = new Size(width, height);
} }
void IDisposable.Dispose() void IDisposable.Dispose()

13
src/Avalonia.Visuals/Media/GlyphRunDrawing.cs

@ -8,9 +8,6 @@
public static readonly StyledProperty<GlyphRun> GlyphRunProperty = public static readonly StyledProperty<GlyphRun> GlyphRunProperty =
AvaloniaProperty.Register<GlyphRunDrawing, GlyphRun>(nameof(GlyphRun)); AvaloniaProperty.Register<GlyphRunDrawing, GlyphRun>(nameof(GlyphRun));
public static readonly StyledProperty<Point> BaselineOriginProperty =
AvaloniaProperty.Register<GlyphRunDrawing, Point>(nameof(BaselineOrigin));
public IBrush Foreground public IBrush Foreground
{ {
get => GetValue(ForegroundProperty); get => GetValue(ForegroundProperty);
@ -23,12 +20,6 @@
set => SetValue(GlyphRunProperty, value); set => SetValue(GlyphRunProperty, value);
} }
public Point BaselineOrigin
{
get => GetValue(BaselineOriginProperty);
set => SetValue(BaselineOriginProperty, value);
}
public override void Draw(DrawingContext context) public override void Draw(DrawingContext context)
{ {
if (GlyphRun == null) if (GlyphRun == null)
@ -36,12 +27,12 @@
return; return;
} }
context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin); context.DrawGlyphRun(Foreground, GlyphRun);
} }
public override Rect GetBounds() public override Rect GetBounds()
{ {
return GlyphRun?.Bounds ?? default; return GlyphRun != null ? new Rect(GlyphRun.Size) : Rect.Empty;
} }
} }
} }

17
src/Avalonia.Visuals/Media/TextDecoration.cs

@ -155,8 +155,7 @@ namespace Avalonia.Media
/// </summary> /// </summary>
/// <param name="drawingContext">The drawing context.</param> /// <param name="drawingContext">The drawing context.</param>
/// <param name="shapedTextCharacters">The shaped characters that are decorated.</param> /// <param name="shapedTextCharacters">The shaped characters that are decorated.</param>
/// <param name="origin">The origin.</param> internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters)
internal void Draw(DrawingContext drawingContext, ShapedTextCharacters shapedTextCharacters, Point origin)
{ {
var fontRenderingEmSize = shapedTextCharacters.Properties.FontRenderingEmSize; var fontRenderingEmSize = shapedTextCharacters.Properties.FontRenderingEmSize;
var fontMetrics = shapedTextCharacters.FontMetrics; var fontMetrics = shapedTextCharacters.FontMetrics;
@ -181,16 +180,20 @@ namespace Avalonia.Media
break; break;
} }
var origin = new Point();
switch (Location) switch (Location)
{ {
case TextDecorationLocation.Overline: case TextDecorationLocation.Baseline:
origin += new Point(0, fontMetrics.Ascent); origin += shapedTextCharacters.GlyphRun.BaselineOrigin;
break; break;
case TextDecorationLocation.Strikethrough: case TextDecorationLocation.Strikethrough:
origin += new Point(0, -fontMetrics.StrikethroughPosition); origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.StrikethroughPosition);
break; break;
case TextDecorationLocation.Underline: case TextDecorationLocation.Underline:
origin += new Point(0, -fontMetrics.UnderlinePosition); origin += new Point(shapedTextCharacters.GlyphRun.BaselineOrigin.X,
shapedTextCharacters.GlyphRun.BaselineOrigin.Y - fontMetrics.UnderlinePosition);
break; break;
} }
@ -207,7 +210,7 @@ namespace Avalonia.Media
var pen = new Pen(Stroke ?? shapedTextCharacters.Properties.ForegroundBrush, thickness, var pen = new Pen(Stroke ?? shapedTextCharacters.Properties.ForegroundBrush, thickness,
new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap); new DashStyle(StrokeDashArray, StrokeDashOffset), StrokeLineCap);
drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Bounds.Width, 0)); drawingContext.DrawLine(pen, origin, origin + new Point(shapedTextCharacters.Size.Width, 0));
} }
} }
} }

7
src/Avalonia.Visuals/Media/TextFormatting/DrawableTextRun.cs

@ -6,15 +6,14 @@
public abstract class DrawableTextRun : TextRun public abstract class DrawableTextRun : TextRun
{ {
/// <summary> /// <summary>
/// Gets the bounds. /// Gets the size.
/// </summary> /// </summary>
public abstract Rect Bounds { get; } public abstract Size Size { get; }
/// <summary> /// <summary>
/// Draws the <see cref="DrawableTextRun"/> at the given origin. /// Draws the <see cref="DrawableTextRun"/> at the given origin.
/// </summary> /// </summary>
/// <param name="drawingContext">The drawing context.</param> /// <param name="drawingContext">The drawing context.</param>
/// <param name="origin">The origin.</param> public abstract void Draw(DrawingContext drawingContext);
public abstract void Draw(DrawingContext drawingContext, Point origin);
} }
} }

11
src/Avalonia.Visuals/Media/TextFormatting/ShapedTextCharacters.cs

@ -26,7 +26,7 @@ namespace Avalonia.Media.TextFormatting
public override int TextSourceLength { get; } public override int TextSourceLength { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override Rect Bounds => GlyphRun.Bounds; public override Size Size => GlyphRun.Size;
/// <summary> /// <summary>
/// Gets the font metrics. /// Gets the font metrics.
@ -45,7 +45,7 @@ namespace Avalonia.Media.TextFormatting
public GlyphRun GlyphRun { get; } public GlyphRun GlyphRun { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override void Draw(DrawingContext drawingContext, Point origin) public override void Draw(DrawingContext drawingContext)
{ {
if (GlyphRun.GlyphIndices.Length == 0) if (GlyphRun.GlyphIndices.Length == 0)
{ {
@ -64,11 +64,10 @@ namespace Avalonia.Media.TextFormatting
if (Properties.BackgroundBrush != null) if (Properties.BackgroundBrush != null)
{ {
drawingContext.DrawRectangle(Properties.BackgroundBrush, null, drawingContext.DrawRectangle(Properties.BackgroundBrush, null, new Rect(Size));
new Rect(origin.X, origin.Y + FontMetrics.Ascent, Bounds.Width, Bounds.Height));
} }
drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun, origin); drawingContext.DrawGlyphRun(Properties.ForegroundBrush, GlyphRun);
if (Properties.TextDecorations == null) if (Properties.TextDecorations == null)
{ {
@ -77,7 +76,7 @@ namespace Avalonia.Media.TextFormatting
foreach (var textDecoration in Properties.TextDecorations) foreach (var textDecoration in Properties.TextDecorations)
{ {
textDecoration.Draw(drawingContext, this, origin); textDecoration.Draw(drawingContext, this);
} }
} }

6
src/Avalonia.Visuals/Media/TextFormatting/TextFormatterImpl.cs

@ -52,7 +52,7 @@ namespace Avalonia.Media.TextFormatting
{ {
var glyphRun = textCharacters.GlyphRun; var glyphRun = textCharacters.GlyphRun;
if (glyphRun.Bounds.Width < availableWidth) if (glyphRun.Size.Width < availableWidth)
{ {
return glyphRun.Characters.Length; return glyphRun.Characters.Length;
} }
@ -348,7 +348,7 @@ namespace Avalonia.Media.TextFormatting
{ {
var currentRun = textRuns[runIndex]; var currentRun = textRuns[runIndex];
if (currentWidth + currentRun.GlyphRun.Bounds.Width > availableWidth) if (currentWidth + currentRun.Size.Width > availableWidth)
{ {
var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth); var measuredLength = MeasureCharacters(currentRun, paragraphWidth - currentWidth);
@ -421,7 +421,7 @@ namespace Avalonia.Media.TextFormatting
return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak); return new TextLineImpl(splitResult.First, textLineMetrics, lineBreak);
} }
currentWidth += currentRun.GlyphRun.Bounds.Width; currentWidth += currentRun.Size.Width;
currentLength += currentRun.GlyphRun.Characters.Length; currentLength += currentRun.GlyphRun.Characters.Length;

10
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -115,22 +115,24 @@ namespace Avalonia.Media.TextFormatting
/// Draws the text layout. /// Draws the text layout.
/// </summary> /// </summary>
/// <param name="context">The drawing context.</param> /// <param name="context">The drawing context.</param>
/// <param name="origin">The origin.</param> public void Draw(DrawingContext context)
public void Draw(DrawingContext context, Point origin)
{ {
if (!TextLines.Any()) if (!TextLines.Any())
{ {
return; return;
} }
var currentY = origin.Y; var currentY = 0.0;
foreach (var textLine in TextLines) foreach (var textLine in TextLines)
{ {
var offsetX = TextLine.GetParagraphOffsetX(textLine.LineMetrics.Size.Width, Size.Width, var offsetX = TextLine.GetParagraphOffsetX(textLine.LineMetrics.Size.Width, Size.Width,
_paragraphProperties.TextAlignment); _paragraphProperties.TextAlignment);
textLine.Draw(context, new Point(origin.X + offsetX, currentY)); using (context.PushPostTransform(Matrix.CreateTranslation(offsetX, currentY)))
{
textLine.Draw(context);
}
currentY += textLine.LineMetrics.Size.Height; currentY += textLine.LineMetrics.Size.Height;
} }

3
src/Avalonia.Visuals/Media/TextFormatting/TextLine.cs

@ -51,8 +51,7 @@ namespace Avalonia.Media.TextFormatting
/// Draws the <see cref="TextLine"/> at the given origin. /// Draws the <see cref="TextLine"/> at the given origin.
/// </summary> /// </summary>
/// <param name="drawingContext">The drawing context.</param> /// <param name="drawingContext">The drawing context.</param>
/// <param name="origin">The origin.</param> public abstract void Draw(DrawingContext drawingContext);
public abstract void Draw(DrawingContext drawingContext, Point origin);
/// <summary> /// <summary>
/// Create a collapsed line based on collapsed text properties. /// Create a collapsed line based on collapsed text properties.

29
src/Avalonia.Visuals/Media/TextFormatting/TextLineImpl.cs

@ -33,17 +33,18 @@ namespace Avalonia.Media.TextFormatting
public override bool HasCollapsed { get; } public override bool HasCollapsed { get; }
/// <inheritdoc/> /// <inheritdoc/>
public override void Draw(DrawingContext drawingContext, Point origin) public override void Draw(DrawingContext drawingContext)
{ {
var currentX = origin.X; var currentX = 0.0;
foreach (var textRun in _textRuns) foreach (var textRun in _textRuns)
{ {
var baselineOrigin = new Point(currentX, origin.Y + LineMetrics.TextBaseline); using (drawingContext.PushPostTransform(Matrix.CreateTranslation(currentX, 0)))
{
textRun.Draw(drawingContext, baselineOrigin); textRun.Draw(drawingContext);
}
currentX += textRun.Bounds.Width; currentX += textRun.Size.Width;
} }
} }
@ -64,13 +65,13 @@ namespace Avalonia.Media.TextFormatting
var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol); var shapedSymbol = CreateShapedSymbol(collapsingProperties.Symbol);
var availableWidth = collapsingProperties.Width - shapedSymbol.Bounds.Width; var availableWidth = collapsingProperties.Width - shapedSymbol.Size.Width;
while (runIndex < _textRuns.Count) while (runIndex < _textRuns.Count)
{ {
var currentRun = _textRuns[runIndex]; var currentRun = _textRuns[runIndex];
currentWidth += currentRun.GlyphRun.Bounds.Width; currentWidth += currentRun.Size.Width;
if (currentWidth > availableWidth) if (currentWidth > availableWidth)
{ {
@ -125,7 +126,7 @@ namespace Avalonia.Media.TextFormatting
return new TextLineImpl(shapedTextCharacters, textLineMetrics, TextLineBreak, true); return new TextLineImpl(shapedTextCharacters, textLineMetrics, TextLineBreak, true);
} }
availableWidth -= currentRun.GlyphRun.Bounds.Width; availableWidth -= currentRun.Size.Width;
collapsedLength += currentRun.GlyphRun.Characters.Length; collapsedLength += currentRun.GlyphRun.Characters.Length;
@ -133,7 +134,7 @@ namespace Avalonia.Media.TextFormatting
} }
textLineMetrics = textLineMetrics =
new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Bounds.Width), new TextLineMetrics(LineMetrics.Size.WithWidth(LineMetrics.Size.Width + shapedSymbol.Size.Width),
LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed); LineMetrics.TextBaseline, TextRange, LineMetrics.HasOverflowed);
return new TextLineImpl(new List<ShapedTextCharacters>(_textRuns) { shapedSymbol }, textLineMetrics, null, return new TextLineImpl(new List<ShapedTextCharacters>(_textRuns) { shapedSymbol }, textLineMetrics, null,
@ -156,12 +157,12 @@ namespace Avalonia.Media.TextFormatting
{ {
characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _); characterHit = run.GlyphRun.GetCharacterHitFromDistance(distance, out _);
if (distance <= run.Bounds.Width) if (distance <= run.Size.Width)
{ {
break; break;
} }
distance -= run.Bounds.Width; distance -= run.Size.Width;
} }
return characterHit; return characterHit;
@ -229,7 +230,7 @@ namespace Avalonia.Media.TextFormatting
{ {
if (codepointIndex > textRun.Text.End) if (codepointIndex > textRun.Text.End)
{ {
currentDistance += textRun.Bounds.Width; currentDistance += textRun.Size.Width;
continue; continue;
} }
@ -405,7 +406,7 @@ namespace Avalonia.Media.TextFormatting
for (var i = 0; i < shapedTextCharacters.Count; i++) for (var i = 0; i < shapedTextCharacters.Count; i++)
{ {
shapedWidth += shapedTextCharacters[i].Bounds.Width; shapedWidth += shapedTextCharacters[i].Size.Width;
} }
return shapedWidth; return shapedWidth;

2
src/Avalonia.Visuals/Media/TextFormatting/TextLineMetrics.cs

@ -67,7 +67,7 @@ namespace Avalonia.Media.TextFormatting
var fontMetrics = var fontMetrics =
new FontMetrics(shapedRun.Properties.Typeface, shapedRun.Properties.FontRenderingEmSize); new FontMetrics(shapedRun.Properties.Typeface, shapedRun.Properties.FontRenderingEmSize);
lineWidth += shapedRun.Bounds.Width; lineWidth += shapedRun.Size.Width;
if (ascent > fontMetrics.Ascent) if (ascent > fontMetrics.Ascent)
{ {

3
src/Avalonia.Visuals/Platform/IDrawingContextImpl.cs

@ -84,8 +84,7 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
/// <param name="foreground">The foreground.</param> /// <param name="foreground">The foreground.</param>
/// <param name="glyphRun">The glyph run.</param> /// <param name="glyphRun">The glyph run.</param>
/// <param name="baselineOrigin">The baseline origin of the glyph run.</param> void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun);
void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin);
/// <summary> /// <summary>
/// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer /// Creates a new <see cref="IRenderTargetBitmapImpl"/> that can be used as a render layer

4
src/Avalonia.Visuals/Rendering/SceneGraph/DeferredDrawingContextImpl.cs

@ -204,13 +204,13 @@ namespace Avalonia.Rendering.SceneGraph
} }
/// <inheritdoc/> /// <inheritdoc/>
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
var next = NextDrawAs<GlyphRunNode>(); var next = NextDrawAs<GlyphRunNode>();
if (next == null || !next.Item.Equals(Transform, foreground, glyphRun)) if (next == null || !next.Item.Equals(Transform, foreground, glyphRun))
{ {
Add(new GlyphRunNode(Transform, foreground, glyphRun, baselineOrigin, CreateChildScene(foreground))); Add(new GlyphRunNode(Transform, foreground, glyphRun, CreateChildScene(foreground)));
} }
else else

13
src/Avalonia.Visuals/Rendering/SceneGraph/GlyphRunNode.cs

@ -1,6 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform; using Avalonia.Platform;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -17,20 +18,17 @@ namespace Avalonia.Rendering.SceneGraph
/// <param name="transform">The transform.</param> /// <param name="transform">The transform.</param>
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="glyphRun">The glyph run to draw.</param> /// <param name="glyphRun">The glyph run to draw.</param>
/// <param name="baselineOrigin">The baseline origin of the glyph run.</param>
/// <param name="childScenes">Child scenes for drawing visual brushes.</param> /// <param name="childScenes">Child scenes for drawing visual brushes.</param>
public GlyphRunNode( public GlyphRunNode(
Matrix transform, Matrix transform,
IBrush foreground, IBrush foreground,
GlyphRun glyphRun, GlyphRun glyphRun,
Point baselineOrigin,
IDictionary<IVisual, Scene> childScenes = null) IDictionary<IVisual, Scene> childScenes = null)
: base(glyphRun.Bounds.Translate(baselineOrigin), transform) : base(new Rect(glyphRun.Size), transform)
{ {
Transform = transform; Transform = transform;
Foreground = foreground?.ToImmutable(); Foreground = foreground?.ToImmutable();
GlyphRun = glyphRun; GlyphRun = glyphRun;
BaselineOrigin = baselineOrigin;
ChildScenes = childScenes; ChildScenes = childScenes;
} }
@ -49,11 +47,6 @@ namespace Avalonia.Rendering.SceneGraph
/// </summary> /// </summary>
public GlyphRun GlyphRun { get; } public GlyphRun GlyphRun { get; }
/// <summary>
/// Gets the baseline origin.
/// </summary>
public Point BaselineOrigin { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public override IDictionary<IVisual, Scene> ChildScenes { get; } public override IDictionary<IVisual, Scene> ChildScenes { get; }
@ -61,7 +54,7 @@ namespace Avalonia.Rendering.SceneGraph
public override void Render(IDrawingContextImpl context) public override void Render(IDrawingContextImpl context)
{ {
context.Transform = Transform; context.Transform = Transform;
context.DrawGlyphRun(Foreground, GlyphRun, BaselineOrigin); context.DrawGlyphRun(Foreground, GlyphRun);
} }
/// <summary> /// <summary>

8
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -401,16 +401,16 @@ namespace Avalonia.Skia
} }
/// <inheritdoc /> /// <inheritdoc />
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Bounds.Size)) using (var paintWrapper = CreatePaint(_fillPaint, foreground, glyphRun.Size))
{ {
var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl; var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
ConfigureTextRendering(paintWrapper); ConfigureTextRendering(paintWrapper);
Canvas.DrawText(glyphRunImpl.TextBlob, (float)baselineOrigin.X, Canvas.DrawText(glyphRunImpl.TextBlob, (float)glyphRun.BaselineOrigin.X,
(float)baselineOrigin.Y, paintWrapper.Paint); (float)glyphRun.BaselineOrigin.Y, paintWrapper.Paint);
} }
} }

7
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -324,13 +324,14 @@ namespace Avalonia.Direct2D1.Media
/// <param name="foreground">The foreground.</param> /// <param name="foreground">The foreground.</param>
/// <param name="glyphRun">The glyph run.</param> /// <param name="glyphRun">The glyph run.</param>
/// <param name="baselineOrigin"></param> /// <param name="baselineOrigin"></param>
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun, Point baselineOrigin) public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{ {
using (var brush = CreateBrush(foreground, glyphRun.Bounds.Size)) using (var brush = CreateBrush(foreground, glyphRun.Size))
{ {
var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl; var glyphRunImpl = (GlyphRunImpl)glyphRun.GlyphRunImpl;
_renderTarget.DrawGlyphRun(baselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun, brush.PlatformBrush, MeasuringMode.Natural); _renderTarget.DrawGlyphRun(glyphRun.BaselineOrigin.ToSharpDX(), glyphRunImpl.GlyphRun,
brush.PlatformBrush, MeasuringMode.Natural);
} }
} }

2
tests/Avalonia.Skia.UnitTests/Media/TextFormatting/TextLayoutTests.cs

@ -369,7 +369,7 @@ namespace Avalonia.Skia.UnitTests.Media.TextFormatting
var glyphRun = shapedRun.GlyphRun; var glyphRun = shapedRun.GlyphRun;
var width = glyphRun.Bounds.Width; var width = glyphRun.Size.Width;
var characterHit = glyphRun.GetCharacterHitFromDistance(width, out _); var characterHit = glyphRun.GetCharacterHitFromDistance(width, out _);

Loading…
Cancel
Save