Browse Source

Ported changes to DrawingContext from scenegraph

Ported changes to `DrawingContext` and `DrawingContextImpl` from the
`scenegraph` branch.
pull/912/head
Steven Kirk 9 years ago
parent
commit
4e7b743ecd
  1. 2
      src/Avalonia.Visuals/Avalonia.Visuals.csproj
  2. 100
      src/Avalonia.Visuals/Media/DrawingContext.cs
  3. 18
      src/Avalonia.Visuals/Media/IDrawingContextImpl.cs
  4. 5
      src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs
  5. 25
      src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs
  6. 8
      src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs
  7. 22
      src/Skia/Avalonia.Skia/DrawingContextImpl.cs
  8. 29
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  9. 2
      src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
  10. 4
      src/Windows/Avalonia.Direct2D1/Media/AvaloniaTextRenderer.cs
  11. 31
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  12. 3
      src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs
  13. 2
      src/Windows/Avalonia.Direct2D1/RenderTarget.cs
  14. 2
      src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

2
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@ -107,7 +107,7 @@
<Compile Include="Media\FormattedTextLine.cs" /> <Compile Include="Media\FormattedTextLine.cs" />
<Compile Include="Media\FormattedText.cs" /> <Compile Include="Media\FormattedText.cs" />
<Compile Include="Media\Geometry.cs" /> <Compile Include="Media\Geometry.cs" />
<Compile Include="Media\IDrawingContext.cs" /> <Compile Include="Media\IDrawingContextImpl.cs" />
<Compile Include="Platform\ExportRenderingSubsystemAttribute.cs" /> <Compile Include="Platform\ExportRenderingSubsystemAttribute.cs" />
<Compile Include="Platform\ILockedFramebuffer.cs" /> <Compile Include="Platform\ILockedFramebuffer.cs" />
<Compile Include="Platform\IModuleEnvironmentChecker.cs" /> <Compile Include="Platform\IModuleEnvironmentChecker.cs" />

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

@ -1,18 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
namespace Avalonia.Media namespace Avalonia.Media
{ {
public sealed class DrawingContext : IDisposable public sealed class DrawingContext : IDisposable
{ {
private readonly IDrawingContextImpl _impl;
private int _currentLevel; private int _currentLevel;
static readonly Stack<Stack<PushedState>> StateStackPool = new Stack<Stack<PushedState>>(); static readonly Stack<Stack<PushedState>> StateStackPool = new Stack<Stack<PushedState>>();
static readonly Stack<Stack<TransformContainer>> TransformStackPool = new Stack<Stack<TransformContainer>>(); static readonly Stack<Stack<TransformContainer>> TransformStackPool = new Stack<Stack<TransformContainer>>();
@ -37,14 +32,15 @@ namespace Avalonia.Media
public DrawingContext(IDrawingContextImpl impl) public DrawingContext(IDrawingContextImpl impl)
{ {
_impl = impl; PlatformImpl = impl;
} }
public IDrawingContextImpl PlatformImpl { get; }
private Matrix _currentTransform = Matrix.Identity; private Matrix _currentTransform = Matrix.Identity;
private Matrix _currentContainerTransform = Matrix.Identity; private Matrix _currentContainerTransform = Matrix.Identity;
/// <summary> /// <summary>
/// Gets the current transform of the drawing context. /// Gets the current transform of the drawing context.
/// </summary> /// </summary>
@ -54,15 +50,15 @@ namespace Avalonia.Media
private set private set
{ {
_currentTransform = value; _currentTransform = value;
var transform = _currentTransform*_currentContainerTransform; var transform = _currentTransform * _currentContainerTransform;
_impl.Transform = transform; PlatformImpl.Transform = transform;
} }
} }
//HACK: This is a temporary hack that is used in the render loop //HACK: This is a temporary hack that is used in the render loop
//to update TransformedBounds property //to update TransformedBounds property
[Obsolete("HACK for render loop, don't use")] [Obsolete("HACK for render loop, don't use")]
internal Matrix CurrentContainerTransform => _currentContainerTransform; internal Matrix CurrentContainerTransform => _currentContainerTransform;
/// <summary> /// <summary>
/// Draws a bitmap image. /// Draws a bitmap image.
@ -72,7 +68,11 @@ namespace Avalonia.Media
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
=> _impl.DrawImage(source, opacity, sourceRect, destRect); {
Contract.Requires<ArgumentNullException>(source != null);
PlatformImpl.DrawImage(source.PlatformImpl, opacity, sourceRect, destRect);
}
/// <summary> /// <summary>
/// Draws a line. /// Draws a line.
@ -80,7 +80,13 @@ namespace Avalonia.Media
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="p1">The first point of the line.</param> /// <param name="p1">The first point of the line.</param>
/// <param name="p2">The second point of the line.</param> /// <param name="p2">The second point of the line.</param>
public void DrawLine(Pen pen, Point p1, Point p2) => _impl.DrawLine(pen, p1, p2); public void DrawLine(Pen pen, Point p1, Point p2)
{
if (PenIsVisible(pen))
{
PlatformImpl.DrawLine(pen, p1, p2);
}
}
/// <summary> /// <summary>
/// Draws a geometry. /// Draws a geometry.
@ -88,7 +94,13 @@ namespace Avalonia.Media
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) => _impl.DrawGeometry(brush, pen, geometry); public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry)
{
if (brush != null || PenIsVisible(pen))
{
PlatformImpl.DrawGeometry(brush, pen, geometry.PlatformImpl);
}
}
/// <summary> /// <summary>
/// Draws the outline of a rectangle. /// Draws the outline of a rectangle.
@ -97,7 +109,12 @@ namespace Avalonia.Media
/// <param name="rect">The rectangle bounds.</param> /// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param> /// <param name="cornerRadius">The corner radius.</param>
public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0.0f) public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0.0f)
=> _impl.DrawRectangle(pen, rect, cornerRadius); {
if (PenIsVisible(pen))
{
PlatformImpl.DrawRectangle(pen, rect, cornerRadius);
}
}
/// <summary> /// <summary>
/// Draws text. /// Draws text.
@ -106,7 +123,14 @@ namespace Avalonia.Media
/// <param name="origin">The upper-left corner of the text.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
public void DrawText(IBrush foreground, Point origin, FormattedText text) public void DrawText(IBrush foreground, Point origin, FormattedText text)
=> _impl.DrawText(foreground, origin, text); {
Contract.Requires<ArgumentNullException>(text != null);
if (foreground != null)
{
PlatformImpl.DrawText(foreground, origin, text.PlatformImpl);
}
}
/// <summary> /// <summary>
/// Draws a filled rectangle. /// Draws a filled rectangle.
@ -115,7 +139,12 @@ namespace Avalonia.Media
/// <param name="rect">The rectangle bounds.</param> /// <param name="rect">The rectangle bounds.</param>
/// <param name="cornerRadius">The corner radius.</param> /// <param name="cornerRadius">The corner radius.</param>
public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f) public void FillRectangle(IBrush brush, Rect rect, float cornerRadius = 0.0f)
=> _impl.FillRectangle(brush, rect, cornerRadius); {
if (brush != null && rect != Rect.Empty)
{
PlatformImpl.FillRectangle(brush, rect, cornerRadius);
}
}
public struct PushedState : IDisposable public struct PushedState : IDisposable
{ {
@ -146,7 +175,7 @@ namespace Avalonia.Media
public void Dispose() public void Dispose()
{ {
if(_type == PushedStateType.None) if (_type == PushedStateType.None)
return; return;
if (_context._currentLevel != _level) if (_context._currentLevel != _level)
throw new InvalidOperationException("Wrong Push/Pop state order"); throw new InvalidOperationException("Wrong Push/Pop state order");
@ -155,13 +184,13 @@ namespace Avalonia.Media
if (_type == PushedStateType.Matrix) if (_type == PushedStateType.Matrix)
_context.CurrentTransform = _matrix; _context.CurrentTransform = _matrix;
else if (_type == PushedStateType.Clip) else if (_type == PushedStateType.Clip)
_context._impl.PopClip(); _context.PlatformImpl.PopClip();
else if (_type == PushedStateType.Opacity) else if (_type == PushedStateType.Opacity)
_context._impl.PopOpacity(); _context.PlatformImpl.PopOpacity();
else if (_type == PushedStateType.GeometryClip) else if (_type == PushedStateType.GeometryClip)
_context._impl.PopGeometryClip(); _context.PlatformImpl.PopGeometryClip();
else if (_type == PushedStateType.OpacityMask) else if (_type == PushedStateType.OpacityMask)
_context._impl.PopOpacityMask(); _context.PlatformImpl.PopOpacityMask();
else if (_type == PushedStateType.MatrixContainer) else if (_type == PushedStateType.MatrixContainer)
{ {
var cont = _context._transformContainers.Pop(); var cont = _context._transformContainers.Pop();
@ -179,7 +208,7 @@ namespace Avalonia.Media
/// <returns>A disposable used to undo the clip rectangle.</returns> /// <returns>A disposable used to undo the clip rectangle.</returns>
public PushedState PushClip(Rect clip) public PushedState PushClip(Rect clip)
{ {
_impl.PushClip(clip); PlatformImpl.PushClip(clip);
return new PushedState(this, PushedState.PushedStateType.Clip); return new PushedState(this, PushedState.PushedStateType.Clip);
} }
@ -191,7 +220,7 @@ namespace Avalonia.Media
public PushedState PushGeometryClip(Geometry clip) public PushedState PushGeometryClip(Geometry clip)
{ {
Contract.Requires<ArgumentNullException>(clip != null); Contract.Requires<ArgumentNullException>(clip != null);
_impl.PushGeometryClip(clip); PlatformImpl.PushGeometryClip(clip.PlatformImpl);
return new PushedState(this, PushedState.PushedStateType.GeometryClip); return new PushedState(this, PushedState.PushedStateType.GeometryClip);
} }
@ -201,9 +230,9 @@ namespace Avalonia.Media
/// <param name="opacity">The opacity.</param> /// <param name="opacity">The opacity.</param>
/// <returns>A disposable used to undo the opacity.</returns> /// <returns>A disposable used to undo the opacity.</returns>
public PushedState PushOpacity(double opacity) public PushedState PushOpacity(double opacity)
//TODO: Eliminate platform-specific push opacity call //TODO: Eliminate platform-specific push opacity call
{ {
_impl.PushOpacity(opacity); PlatformImpl.PushOpacity(opacity);
return new PushedState(this, PushedState.PushedStateType.Opacity); return new PushedState(this, PushedState.PushedStateType.Opacity);
} }
@ -217,7 +246,7 @@ namespace Avalonia.Media
/// <returns>A disposable to undo the opacity mask.</returns> /// <returns>A disposable to undo the opacity mask.</returns>
public PushedState PushOpacityMask(IBrush mask, Rect bounds) public PushedState PushOpacityMask(IBrush mask, Rect bounds)
{ {
_impl.PushOpacityMask(mask, bounds); PlatformImpl.PushOpacityMask(mask, bounds);
return new PushedState(this, PushedState.PushedStateType.OpacityMask); return new PushedState(this, PushedState.PushedStateType.OpacityMask);
} }
@ -226,14 +255,14 @@ namespace Avalonia.Media
/// </summary> /// </summary>
/// <param name="matrix">The matrix</param> /// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns> /// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform*matrix); public PushedState PushPostTransform(Matrix matrix) => PushSetTransform(CurrentTransform * matrix);
/// <summary> /// <summary>
/// Pushes a matrix pre-transformation. /// Pushes a matrix pre-transformation.
/// </summary> /// </summary>
/// <param name="matrix">The matrix</param> /// <param name="matrix">The matrix</param>
/// <returns>A disposable used to undo the transformation.</returns> /// <returns>A disposable used to undo the transformation.</returns>
public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix*CurrentTransform); public PushedState PushPreTransform(Matrix matrix) => PushSetTransform(matrix * CurrentTransform);
/// <summary> /// <summary>
/// Sets the current matrix transformation. /// Sets the current matrix transformation.
@ -244,7 +273,7 @@ namespace Avalonia.Media
{ {
var oldMatrix = CurrentTransform; var oldMatrix = CurrentTransform;
CurrentTransform = matrix; CurrentTransform = matrix;
return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix); return new PushedState(this, PushedState.PushedStateType.Matrix, oldMatrix);
} }
@ -255,7 +284,7 @@ namespace Avalonia.Media
public PushedState PushTransformContainer() public PushedState PushTransformContainer()
{ {
_transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform)); _transformContainers.Push(new TransformContainer(CurrentTransform, _currentContainerTransform));
_currentContainerTransform = CurrentTransform*_currentContainerTransform; _currentContainerTransform = CurrentTransform * _currentContainerTransform;
_currentTransform = Matrix.Identity; _currentTransform = Matrix.Identity;
return new PushedState(this, PushedState.PushedStateType.MatrixContainer); return new PushedState(this, PushedState.PushedStateType.MatrixContainer);
} }
@ -271,7 +300,12 @@ namespace Avalonia.Media
_states = null; _states = null;
TransformStackPool.Push(_transformContainers); TransformStackPool.Push(_transformContainers);
_transformContainers = null; _transformContainers = null;
_impl.Dispose(); PlatformImpl.Dispose();
}
private static bool PenIsVisible(Pen pen)
{
return pen?.Brush != null && pen.Thickness > 0;
} }
} }
} }

18
src/Avalonia.Visuals/Media/IDrawingContext.cs → src/Avalonia.Visuals/Media/IDrawingContextImpl.cs

@ -2,7 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using Avalonia.Media.Imaging; using Avalonia.Platform;
namespace Avalonia.Media namespace Avalonia.Media
{ {
@ -16,6 +16,12 @@ namespace Avalonia.Media
/// </summary> /// </summary>
Matrix Transform { get; set; } Matrix Transform { get; set; }
/// <summary>
/// Clears the render target to the specified color.
/// </summary>
/// <param name="color">The color.</param>
void Clear(Color color);
/// <summary> /// <summary>
/// Draws a bitmap image. /// Draws a bitmap image.
/// </summary> /// </summary>
@ -23,7 +29,7 @@ namespace Avalonia.Media
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect); void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect);
/// <summary> /// <summary>
/// Draws a line. /// Draws a line.
@ -39,7 +45,7 @@ namespace Avalonia.Media
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
void DrawGeometry(IBrush brush, Pen pen, Geometry geometry); void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry);
/// <summary> /// <summary>
/// Draws the outline of a rectangle. /// Draws the outline of a rectangle.
@ -55,7 +61,7 @@ namespace Avalonia.Media
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="origin">The upper-left corner of the text.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
void DrawText(IBrush foreground, Point origin, FormattedText text); void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text);
/// <summary> /// <summary>
/// Draws a filled rectangle. /// Draws a filled rectangle.
@ -89,8 +95,8 @@ namespace Avalonia.Media
/// Pushes a clip geometry. /// Pushes a clip geometry.
/// </summary> /// </summary>
/// <param name="clip">The clip geometry.</param> /// <param name="clip">The clip geometry.</param>
void PushGeometryClip(Geometry clip); void PushGeometryClip(IGeometryImpl clip);
void PopGeometryClip(); void PopGeometryClip();
} }
} }

5
src/Avalonia.Visuals/Platform/IFormattedTextImpl.cs

@ -17,6 +17,11 @@ namespace Avalonia.Platform
/// </summary> /// </summary>
Size Constraint { get; set; } Size Constraint { get; set; }
/// <summary>
/// Gets the text.
/// </summary>
string Text { get; }
/// <summary> /// <summary>
/// Gets the lines in the text. /// Gets the lines in the text.
/// </summary> /// </summary>

25
src/Gtk/Avalonia.Cairo/Media/DrawingContext.cs

@ -5,13 +5,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reactive.Disposables; using System.Reactive.Disposables;
using System.Runtime.InteropServices;
using Avalonia.Cairo.Media.Imaging; using Avalonia.Cairo.Media.Imaging;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform;
namespace Avalonia.Cairo.Media namespace Avalonia.Cairo.Media
{ {
using Avalonia.Media.Imaging;
using Cairo = global::Cairo; using Cairo = global::Cairo;
/// <summary> /// <summary>
@ -60,6 +59,12 @@ namespace Avalonia.Cairo.Media
} }
} }
public void Clear(Color color)
{
_context.SetSourceRGBA(color.R, color.G, color.B, color.A);
_context.Paint();
}
/// <summary> /// <summary>
/// Ends a draw operation. /// Ends a draw operation.
/// </summary> /// </summary>
@ -75,9 +80,9 @@ namespace Avalonia.Cairo.Media
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
public void DrawImage(IBitmap bitmap, double opacity, Rect sourceRect, Rect destRect) public void DrawImage(IBitmapImpl bitmap, double opacity, Rect sourceRect, Rect destRect)
{ {
var impl = bitmap.PlatformImpl as BitmapImpl; var impl = bitmap as BitmapImpl;
var size = new Size(impl.PixelWidth, impl.PixelHeight); var size = new Size(impl.PixelWidth, impl.PixelHeight);
var scale = new Vector(destRect.Width / sourceRect.Width, destRect.Height / sourceRect.Height); var scale = new Vector(destRect.Width / sourceRect.Width, destRect.Height / sourceRect.Height);
@ -137,9 +142,9 @@ namespace Avalonia.Cairo.Media
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
{ {
var impl = geometry.PlatformImpl as StreamGeometryImpl; var impl = geometry as StreamGeometryImpl;
var oldMatrix = Transform; var oldMatrix = Transform;
Transform = impl.Transform * Transform; Transform = impl.Transform * Transform;
@ -192,9 +197,9 @@ namespace Avalonia.Cairo.Media
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="origin">The upper-left corner of the text.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
public void DrawText(IBrush foreground, Point origin, FormattedText text) public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{ {
var layout = ((FormattedTextImpl)text.PlatformImpl).Layout; var layout = ((FormattedTextImpl)text).Layout;
_context.MoveTo(origin.X, origin.Y); _context.MoveTo(origin.X, origin.Y);
using (var b = SetBrush(foreground, new Size(0, 0))) using (var b = SetBrush(foreground, new Size(0, 0)))
@ -350,10 +355,10 @@ namespace Avalonia.Cairo.Media
return SetBrush(pen.Brush, destinationSize); return SetBrush(pen.Brush, destinationSize);
} }
public void PushGeometryClip(Geometry clip) public void PushGeometryClip(IGeometryImpl clip)
{ {
_context.Save(); _context.Save();
_context.AppendPath(((StreamGeometryImpl)clip.PlatformImpl).Path); _context.AppendPath(((StreamGeometryImpl)clip).Path);
_context.Clip(); _context.Clip();
} }

8
src/Gtk/Avalonia.Cairo/Media/FormattedTextImpl.cs

@ -13,7 +13,6 @@ namespace Avalonia.Cairo.Media
public class FormattedTextImpl : IFormattedTextImpl public class FormattedTextImpl : IFormattedTextImpl
{ {
private Size _size; private Size _size;
private readonly string _text;
static double CorrectScale(double input) static double CorrectScale(double input)
{ {
@ -32,7 +31,6 @@ namespace Avalonia.Cairo.Media
Contract.Requires<ArgumentNullException>(context != null); Contract.Requires<ArgumentNullException>(context != null);
Contract.Requires<ArgumentNullException>(text != null); Contract.Requires<ArgumentNullException>(text != null);
Layout = new Pango.Layout(context); Layout = new Pango.Layout(context);
_text = text;
Layout.SetText(text); Layout.SetText(text);
Layout.FontDescription = new Pango.FontDescription Layout.FontDescription = new Pango.FontDescription
{ {
@ -46,6 +44,8 @@ namespace Avalonia.Cairo.Media
Layout.Attributes = new Pango.AttrList(); Layout.Attributes = new Pango.AttrList();
} }
public string Text => Layout.Text;
public Size Constraint public Size Constraint
{ {
get get
@ -99,7 +99,7 @@ namespace Avalonia.Cairo.Media
int PangoIndexToTextIndex(int pangoIndex) int PangoIndexToTextIndex(int pangoIndex)
{ {
return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(_text), 0, Math.Min(pangoIndex, _text.Length)).Length; return Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(Text), 0, Math.Min(pangoIndex, Text.Length)).Length;
} }
public Rect HitTestTextPosition(int index) public Rect HitTestTextPosition(int index)
@ -109,7 +109,7 @@ namespace Avalonia.Cairo.Media
int TextIndexToPangoIndex(int textIndex) int TextIndexToPangoIndex(int textIndex)
{ {
return Encoding.UTF8.GetByteCount(textIndex < _text.Length ? _text.Remove(textIndex) : _text); return Encoding.UTF8.GetByteCount(textIndex < Text.Length ? Text.Remove(textIndex) : Text);
} }
public IEnumerable<Rect> HitTestTextRange(int index, int length) public IEnumerable<Rect> HitTestTextRange(int index, int length)

22
src/Skia/Avalonia.Skia/DrawingContextImpl.cs

@ -5,6 +5,7 @@ using SkiaSharp;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Platform;
namespace Avalonia.Skia namespace Avalonia.Skia
{ {
@ -26,9 +27,14 @@ namespace Avalonia.Skia
Transform = Matrix.Identity; Transform = Matrix.Identity;
} }
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) public void Clear(Color color)
{ {
var impl = (BitmapImpl)source.PlatformImpl; Canvas.Clear(color.ToSKColor());
}
public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{
var impl = (BitmapImpl)source;
var s = sourceRect.ToSKRect(); var s = sourceRect.ToSKRect();
var d = destRect.ToSKRect(); var d = destRect.ToSKRect();
using (var paint = new SKPaint() using (var paint = new SKPaint()
@ -46,9 +52,9 @@ namespace Avalonia.Skia
} }
} }
public void DrawGeometry(IBrush brush, Pen pen, Geometry geometry) public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
{ {
var impl = ((StreamGeometryImpl)geometry.PlatformImpl); var impl = (StreamGeometryImpl)geometry;
var size = geometry.Bounds.Size; var size = geometry.Bounds.Size;
using (var fill = brush != null ? CreatePaint(brush, size) : default(PaintWrapper)) using (var fill = brush != null ? CreatePaint(brush, size) : default(PaintWrapper))
@ -284,11 +290,11 @@ namespace Avalonia.Skia
} }
} }
public void DrawText(IBrush foreground, Point origin, FormattedText text) public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{ {
using (var paint = CreatePaint(foreground, text.Measure())) using (var paint = CreatePaint(foreground, text.Measure()))
{ {
var textImpl = text.PlatformImpl as FormattedTextImpl; var textImpl = (FormattedTextImpl)text;
textImpl.Draw(this, Canvas, origin.ToSKPoint(), paint); textImpl.Draw(this, Canvas, origin.ToSKPoint(), paint);
} }
} }
@ -325,10 +331,10 @@ namespace Avalonia.Skia
disposable?.Dispose(); disposable?.Dispose();
} }
public void PushGeometryClip(Geometry clip) public void PushGeometryClip(IGeometryImpl clip)
{ {
Canvas.Save(); Canvas.Save();
Canvas.ClipPath(((StreamGeometryImpl)clip.PlatformImpl).EffectivePath); Canvas.ClipPath(((StreamGeometryImpl)clip).EffectivePath);
} }
public void PopGeometryClip() public void PopGeometryClip()

29
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -15,10 +15,10 @@ namespace Avalonia.Skia
public FormattedTextImpl(string text, string fontFamilyName, double fontSize, FontStyle fontStyle, public FormattedTextImpl(string text, string fontFamilyName, double fontSize, FontStyle fontStyle,
TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping) TextAlignment textAlignment, FontWeight fontWeight, TextWrapping wrapping)
{ {
_text = text ?? string.Empty; Text = text ?? string.Empty;
// 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);
var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight); var typeface = TypefaceCache.GetTypeface(fontFamilyName, fontStyle, fontWeight);
@ -54,6 +54,8 @@ namespace Avalonia.Skia
} }
} }
public string Text { get; }
public void Dispose() public void Dispose()
{ {
} }
@ -98,7 +100,7 @@ namespace Avalonia.Skia
{ {
IsInside = false, IsInside = false,
TextPosition = line.Start + offset, TextPosition = line.Start + offset,
IsTrailing = _text.Length == (line.Start + offset + 1) IsTrailing = Text.Length == (line.Start + offset + 1)
}; };
} }
@ -108,7 +110,7 @@ namespace Avalonia.Skia
{ {
IsInside = false, IsInside = false,
IsTrailing = end, IsTrailing = end,
TextPosition = end ? _text.Length - 1 : 0 TextPosition = end ? Text.Length - 1 : 0
}; };
} }
@ -182,7 +184,7 @@ namespace Avalonia.Skia
public override string ToString() public override string ToString()
{ {
return _text; return Text;
} }
internal void Draw(DrawingContextImpl context, internal void Draw(DrawingContextImpl context,
@ -231,7 +233,7 @@ namespace Avalonia.Skia
if (!hasCusomFGBrushes) if (!hasCusomFGBrushes)
{ {
var subString = _text.Substring(line.Start, line.Length); var subString = Text.Substring(line.Start, line.Length);
canvas.DrawText(subString, x, origin.Y + line.Top + _lineOffset, paint); canvas.DrawText(subString, x, origin.Y + line.Top + _lineOffset, paint);
} }
else else
@ -255,7 +257,7 @@ namespace Avalonia.Skia
currentWrapper = foreground; currentWrapper = foreground;
} }
subStr = _text.Substring(i, len); subStr = Text.Substring(i, len);
if (currFGPaint != currentWrapper.Paint) if (currFGPaint != currentWrapper.Paint)
{ {
@ -284,7 +286,6 @@ namespace Avalonia.Skia
private readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>(); private readonly List<FormattedTextLine> _lines = new List<FormattedTextLine>();
private readonly SKPaint _paint; private readonly SKPaint _paint;
private readonly List<Rect> _rects = new List<Rect>(); private readonly List<Rect> _rects = new List<Rect>();
private readonly string _text;
private readonly TextWrapping _wrapping; private readonly TextWrapping _wrapping;
private Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity); private Size _constraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
private float _lineHeight = 0; private float _lineHeight = 0;
@ -434,7 +435,7 @@ namespace Avalonia.Skia
for (int i = line.Start; i < line.Start + line.TextLength; i++) for (int i = line.Start; i < line.Start + line.TextLength; i++)
{ {
float w = _paint.MeasureText(_text[i].ToString()); float w = _paint.MeasureText(Text[i].ToString());
_rects.Add(new Rect( _rects.Add(new Rect(
prevRight, prevRight,
@ -490,7 +491,7 @@ namespace Avalonia.Skia
private List<Rect> GetRects() private List<Rect> GetRects()
{ {
if (_text.Length > _rects.Count) if (Text.Length > _rects.Count)
{ {
BuildRects(); BuildRects();
} }
@ -500,7 +501,7 @@ namespace Avalonia.Skia
private void Rebuild() private void Rebuild()
{ {
var length = _text.Length; var length = Text.Length;
_lines.Clear(); _lines.Clear();
_rects.Clear(); _rects.Clear();
@ -536,7 +537,7 @@ namespace Avalonia.Skia
int measured; int measured;
int trailingnumber = 0; int trailingnumber = 0;
subString = _text.Substring(curOff); subString = Text.Substring(curOff);
float constraint = -1; float constraint = -1;
@ -547,12 +548,12 @@ namespace Avalonia.Skia
constraint = MAX_LINE_WIDTH; constraint = MAX_LINE_WIDTH;
} }
measured = LineBreak(_text, curOff, length, _paint, constraint, out trailingnumber); measured = LineBreak(Text, curOff, length, _paint, constraint, out trailingnumber);
AvaloniaFormattedTextLine line = new AvaloniaFormattedTextLine(); AvaloniaFormattedTextLine line = new AvaloniaFormattedTextLine();
line.TextLength = measured; line.TextLength = measured;
subString = _text.Substring(line.Start, line.TextLength); subString = Text.Substring(line.Start, line.TextLength);
lineWidth = _paint.MeasureText(subString); lineWidth = _paint.MeasureText(subString);
line.Start = curOff; line.Start = curOff;
line.Length = measured - trailingnumber; line.Length = measured - trailingnumber;

2
src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj

@ -65,7 +65,7 @@
<Compile Include="HwndRenderTarget.cs" /> <Compile Include="HwndRenderTarget.cs" />
<Compile Include="Media\BrushImpl.cs" /> <Compile Include="Media\BrushImpl.cs" />
<Compile Include="Media\BrushWrapper.cs" /> <Compile Include="Media\BrushWrapper.cs" />
<Compile Include="Media\DrawingContext.cs" /> <Compile Include="Media\DrawingContextImpl.cs" />
<Compile Include="Media\Imaging\BitmapImpl.cs" /> <Compile Include="Media\Imaging\BitmapImpl.cs" />
<Compile Include="Media\Imaging\D2DBitmapImpl.cs" /> <Compile Include="Media\Imaging\D2DBitmapImpl.cs" />
<Compile Include="Media\Imaging\RenderTargetBitmapImpl.cs" /> <Compile Include="Media\Imaging\RenderTargetBitmapImpl.cs" />

4
src/Windows/Avalonia.Direct2D1/Media/AvaloniaTextRenderer.cs

@ -11,14 +11,14 @@ namespace Avalonia.Direct2D1.Media
{ {
internal class AvaloniaTextRenderer : TextRenderer internal class AvaloniaTextRenderer : TextRenderer
{ {
private readonly DrawingContext _context; private readonly DrawingContextImpl _context;
private readonly SharpDX.Direct2D1.RenderTarget _renderTarget; private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
private readonly Brush _foreground; private readonly Brush _foreground;
public AvaloniaTextRenderer( public AvaloniaTextRenderer(
DrawingContext context, DrawingContextImpl context,
SharpDX.Direct2D1.RenderTarget target, SharpDX.Direct2D1.RenderTarget target,
Brush foreground) Brush foreground)
{ {

31
src/Windows/Avalonia.Direct2D1/Media/DrawingContext.cs → src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -5,6 +5,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Platform;
using SharpDX; using SharpDX;
using SharpDX.Direct2D1; using SharpDX.Direct2D1;
using SharpDX.Mathematics.Interop; using SharpDX.Mathematics.Interop;
@ -15,7 +16,7 @@ namespace Avalonia.Direct2D1.Media
/// <summary> /// <summary>
/// Draws using Direct2D1. /// Draws using Direct2D1.
/// </summary> /// </summary>
public class DrawingContext : IDrawingContextImpl, IDisposable public class DrawingContextImpl : IDrawingContextImpl, IDisposable
{ {
/// <summary> /// <summary>
/// The Direct2D1 render target. /// The Direct2D1 render target.
@ -30,12 +31,12 @@ namespace Avalonia.Direct2D1.Media
private SharpDX.DXGI.SwapChain1 _swapChain; private SharpDX.DXGI.SwapChain1 _swapChain;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DrawingContext"/> class. /// Initializes a new instance of the <see cref="DrawingContextImpl"/> class.
/// </summary> /// </summary>
/// <param name="renderTarget">The render target to draw to.</param> /// <param name="renderTarget">The render target to draw to.</param>
/// <param name="directWriteFactory">The DirectWrite factory.</param> /// <param name="directWriteFactory">The DirectWrite factory.</param>
/// <param name="swapChain">An optional swap chain associated with this drawing context.</param> /// <param name="swapChain">An optional swap chain associated with this drawing context.</param>
public DrawingContext( public DrawingContextImpl(
SharpDX.Direct2D1.RenderTarget renderTarget, SharpDX.Direct2D1.RenderTarget renderTarget,
SharpDX.DirectWrite.Factory directWriteFactory, SharpDX.DirectWrite.Factory directWriteFactory,
SharpDX.DXGI.SwapChain1 swapChain = null) SharpDX.DXGI.SwapChain1 swapChain = null)
@ -55,6 +56,12 @@ namespace Avalonia.Direct2D1.Media
set { _renderTarget.Transform = value.ToDirect2D(); } set { _renderTarget.Transform = value.ToDirect2D(); }
} }
/// <inheritdoc/>
public void Clear(Color color)
{
_renderTarget.Clear(color.ToDirect2D());
}
/// <summary> /// <summary>
/// Ends a draw operation. /// Ends a draw operation.
/// </summary> /// </summary>
@ -81,9 +88,9 @@ namespace Avalonia.Direct2D1.Media
/// <param name="opacity">The opacity to draw with.</param> /// <param name="opacity">The opacity to draw with.</param>
/// <param name="sourceRect">The rect in the image to draw.</param> /// <param name="sourceRect">The rect in the image to draw.</param>
/// <param name="destRect">The rect in the output to draw to.</param> /// <param name="destRect">The rect in the output to draw to.</param>
public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect) public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
{ {
var impl = (BitmapImpl)source.PlatformImpl; var impl = (BitmapImpl)source;
Bitmap d2d = impl.GetDirect2DBitmap(_renderTarget); Bitmap d2d = impl.GetDirect2DBitmap(_renderTarget);
_renderTarget.DrawBitmap( _renderTarget.DrawBitmap(
d2d, d2d,
@ -127,7 +134,7 @@ namespace Avalonia.Direct2D1.Media
/// <param name="brush">The fill brush.</param> /// <param name="brush">The fill brush.</param>
/// <param name="pen">The stroke pen.</param> /// <param name="pen">The stroke pen.</param>
/// <param name="geometry">The geometry.</param> /// <param name="geometry">The geometry.</param>
public void DrawGeometry(IBrush brush, Pen pen, Avalonia.Media.Geometry geometry) public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
{ {
if (brush != null) if (brush != null)
{ {
@ -135,7 +142,7 @@ namespace Avalonia.Direct2D1.Media
{ {
if (d2dBrush.PlatformBrush != null) if (d2dBrush.PlatformBrush != null)
{ {
var impl = (GeometryImpl)geometry.PlatformImpl; var impl = (GeometryImpl)geometry;
_renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush); _renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
} }
} }
@ -148,7 +155,7 @@ namespace Avalonia.Direct2D1.Media
{ {
if (d2dBrush.PlatformBrush != null) if (d2dBrush.PlatformBrush != null)
{ {
var impl = (GeometryImpl)geometry.PlatformImpl; var impl = (GeometryImpl)geometry;
_renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke); _renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
} }
} }
@ -194,11 +201,11 @@ namespace Avalonia.Direct2D1.Media
/// <param name="foreground">The foreground brush.</param> /// <param name="foreground">The foreground brush.</param>
/// <param name="origin">The upper-left corner of the text.</param> /// <param name="origin">The upper-left corner of the text.</param>
/// <param name="text">The text.</param> /// <param name="text">The text.</param>
public void DrawText(IBrush foreground, Point origin, FormattedText text) public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{ {
if (!string.IsNullOrEmpty(text.Text)) if (!string.IsNullOrEmpty(text.Text))
{ {
var impl = (FormattedTextImpl)text.PlatformImpl; var impl = (FormattedTextImpl)text;
using (var brush = CreateBrush(foreground, impl.Measure())) using (var brush = CreateBrush(foreground, impl.Measure()))
using (var renderer = new AvaloniaTextRenderer(this, _renderTarget, brush.PlatformBrush)) using (var renderer = new AvaloniaTextRenderer(this, _renderTarget, brush.PlatformBrush))
@ -343,14 +350,14 @@ namespace Avalonia.Direct2D1.Media
} }
} }
public void PushGeometryClip(Avalonia.Media.Geometry clip) public void PushGeometryClip(IGeometryImpl clip)
{ {
var parameters = new LayerParameters var parameters = new LayerParameters
{ {
ContentBounds = PrimitiveExtensions.RectangleInfinite, ContentBounds = PrimitiveExtensions.RectangleInfinite,
MaskTransform = PrimitiveExtensions.Matrix3x2Identity, MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
Opacity = 1, Opacity = 1,
GeometricMask = ((GeometryImpl)clip.PlatformImpl).Geometry GeometricMask = ((GeometryImpl)clip).Geometry
}; };
var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget); var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget);
_renderTarget.PushLayer(ref parameters, layer); _renderTarget.PushLayer(ref parameters, layer);

3
src/Windows/Avalonia.Direct2D1/Media/FormattedTextImpl.cs

@ -30,6 +30,7 @@ namespace Avalonia.Direct2D1.Media
(DWrite.FontStyle)fontStyle, (DWrite.FontStyle)fontStyle,
(float)fontSize)) (float)fontSize))
{ {
Text = text;
format.WordWrapping = wrapping == TextWrapping.Wrap ? format.WordWrapping = wrapping == TextWrapping.Wrap ?
DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap; DWrite.WordWrapping.Wrap : DWrite.WordWrapping.NoWrap;
@ -58,6 +59,8 @@ namespace Avalonia.Direct2D1.Media
} }
} }
public string Text { get; }
public DWrite.TextLayout TextLayout { get; } public DWrite.TextLayout TextLayout { get; }
public void Dispose() public void Dispose()

2
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@ -51,7 +51,7 @@ namespace Avalonia.Direct2D1
/// <returns>An <see cref="Avalonia.Media.DrawingContext"/>.</returns> /// <returns>An <see cref="Avalonia.Media.DrawingContext"/>.</returns>
public DrawingContext CreateDrawingContext() public DrawingContext CreateDrawingContext()
{ {
return new DrawingContext(new Media.DrawingContext(_renderTarget, DirectWriteFactory)); return new DrawingContext(new Media.DrawingContextImpl(_renderTarget, DirectWriteFactory));
} }
public void Dispose() public void Dispose()

2
src/Windows/Avalonia.Direct2D1/SwapChainRenderTarget.cs

@ -69,7 +69,7 @@ namespace Avalonia.Direct2D1
CreateSwapChain(); CreateSwapChain();
} }
return new DrawingContext(new Media.DrawingContext(_deviceContext, DirectWriteFactory, _swapChain)); return new DrawingContext(new Media.DrawingContextImpl(_deviceContext, DirectWriteFactory, _swapChain));
} }
public void Dispose() public void Dispose()

Loading…
Cancel
Save