A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

477 lines
14 KiB

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Headless
{
internal class HeadlessPlatformRenderInterface : IPlatformRenderInterface
{
public static void Initialize()
{
AvaloniaLocator.CurrentMutable
.Bind<IPlatformRenderInterface>().ToConstant(new HeadlessPlatformRenderInterface());
}
public IEnumerable<string> InstalledFontNames { get; } = new[] { "Tahoma" };
public bool SupportsIndividualRoundRects => false;
public AlphaFormat DefaultAlphaFormat => AlphaFormat.Premul;
public PixelFormat DefaultPixelFormat => PixelFormat.Rgba8888;
public IFormattedTextImpl CreateFormattedText(string text, Typeface typeface, double fontSize, TextAlignment textAlignment, TextWrapping wrapping, Size constraint, IReadOnlyList<FormattedTextStyleSpan> spans)
{
return new HeadlessFormattedTextStub(text, constraint);
}
public IGeometryImpl CreateEllipseGeometry(Rect rect) => new HeadlessGeometryStub(rect);
public IGeometryImpl CreateLineGeometry(Point p1, Point p2)
{
var tl = new Point(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y));
var br = new Point(Math.Max(p1.X, p2.X), Math.Max(p1.Y, p2.Y));
return new HeadlessGeometryStub(new Rect(tl, br));
}
public IGeometryImpl CreateRectangleGeometry(Rect rect)
{
return new HeadlessGeometryStub(rect);
}
public IStreamGeometryImpl CreateStreamGeometry() => new HeadlessStreamingGeometryStub();
public IRenderTarget CreateRenderTarget(IEnumerable<object> surfaces) => new HeadlessRenderTarget();
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(PixelSize size, Vector dpi)
{
return new HeadlessBitmapStub(size, dpi);
}
public IWriteableBitmapImpl CreateWriteableBitmap(PixelSize size, Vector dpi, PixelFormat format, AlphaFormat alphaFormat)
{
return new HeadlessBitmapStub(size, dpi);
}
public IBitmapImpl LoadBitmap(string fileName)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IBitmapImpl LoadBitmap(Stream stream)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IBitmapImpl LoadBitmap(PixelFormat format, AlphaFormat alphaFormat, IntPtr data, PixelSize size, Vector dpi, int stride)
{
return new HeadlessBitmapStub(new Size(1, 1), new Vector(96, 96));
}
public IBitmapImpl LoadBitmapToWidth(Stream stream, int width, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new HeadlessBitmapStub(new Size(width, width), new Vector(96, 96));
}
public IBitmapImpl LoadBitmapToHeight(Stream stream, int height, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new HeadlessBitmapStub(new Size(height, height), new Vector(96, 96));
}
public IBitmapImpl ResizeBitmap(IBitmapImpl bitmapImpl, PixelSize destinationSize, BitmapInterpolationMode interpolationMode = BitmapInterpolationMode.HighQuality)
{
return new HeadlessBitmapStub(destinationSize, new Vector(96, 96));
}
public IGlyphRunImpl CreateGlyphRun(GlyphRun glyphRun, out double width)
{
width = 100;
return new HeadlessGlyphRunStub();
}
class HeadlessGeometryStub : IGeometryImpl
{
public HeadlessGeometryStub(Rect bounds)
{
Bounds = bounds;
}
public Rect Bounds { get; set; }
public double ContourLength { get; } = 0;
public virtual bool FillContains(Point point) => Bounds.Contains(point);
public Rect GetRenderBounds(IPen pen)
{
if(pen is null)
{
return Bounds;
}
return Bounds.Inflate(pen.Thickness / 2);
}
public bool StrokeContains(IPen pen, Point point)
{
return false;
}
public IGeometryImpl Intersect(IGeometryImpl geometry)
=> new HeadlessGeometryStub(geometry.Bounds.Intersect(Bounds));
public ITransformedGeometryImpl WithTransform(Matrix transform) =>
new HeadlessTransformedGeometryStub(this, transform);
public bool TryGetPointAtDistance(double distance, out Point point)
{
point = new Point();
return false;
}
public bool TryGetPointAndTangentAtDistance(double distance, out Point point, out Point tangent)
{
point = new Point();
tangent = new Point();
return false;
}
public bool TryGetSegment(double startDistance, double stopDistance, bool startOnBeginFigure, out IGeometryImpl segmentGeometry)
{
segmentGeometry = null;
return false;
}
}
class HeadlessTransformedGeometryStub : HeadlessGeometryStub, ITransformedGeometryImpl
{
public HeadlessTransformedGeometryStub(IGeometryImpl b, Matrix transform) : this(Fix(b, transform))
{
}
static (IGeometryImpl, Matrix, Rect) Fix(IGeometryImpl b, Matrix transform)
{
if (b is HeadlessTransformedGeometryStub transformed)
{
b = transformed.SourceGeometry;
transform = transformed.Transform * transform;
}
return (b, transform, b.Bounds.TransformToAABB(transform));
}
private HeadlessTransformedGeometryStub((IGeometryImpl b, Matrix transform, Rect bounds) fix) : base(fix.bounds)
{
SourceGeometry = fix.b;
Transform = fix.transform;
}
public IGeometryImpl SourceGeometry { get; }
public Matrix Transform { get; }
}
class HeadlessGlyphRunStub : IGlyphRunImpl
{
public void Dispose()
{
}
}
class HeadlessStreamingGeometryStub : HeadlessGeometryStub, IStreamGeometryImpl
{
public HeadlessStreamingGeometryStub() : base(Rect.Empty)
{
}
public IStreamGeometryImpl Clone()
{
return this;
}
public IStreamGeometryContextImpl Open()
{
return new HeadlessStreamingGeometryContextStub(this);
}
class HeadlessStreamingGeometryContextStub : IStreamGeometryContextImpl
{
private readonly HeadlessStreamingGeometryStub _parent;
private double _x1, _y1, _x2, _y2;
public HeadlessStreamingGeometryContextStub(HeadlessStreamingGeometryStub parent)
{
_parent = parent;
}
void Track(Point pt)
{
if (_x1 > pt.X)
_x1 = pt.X;
if (_x2 < pt.X)
_x2 = pt.X;
if (_y1 > pt.Y)
_y1 = pt.Y;
if (_y2 < pt.Y)
_y2 = pt.Y;
}
public void Dispose()
{
_parent.Bounds = new Rect(_x1, _y1, _x2 - _x1, _y2 - _y1);
}
public void ArcTo(Point point, Size size, double rotationAngle, bool isLargeArc, SweepDirection sweepDirection)
=> Track(point);
public void BeginFigure(Point startPoint, bool isFilled = true) => Track(startPoint);
public void CubicBezierTo(Point point1, Point point2, Point point3)
{
Track(point1);
Track(point2);
Track(point3);
}
public void QuadraticBezierTo(Point control, Point endPoint)
{
Track(control);
Track(endPoint);
}
public void LineTo(Point point) => Track(point);
public void EndFigure(bool isClosed)
{
Dispose();
}
public void SetFillRule(FillRule fillRule)
{
}
}
}
class HeadlessBitmapStub : IBitmapImpl, IDrawingContextLayerImpl, IWriteableBitmapImpl
{
public Size Size { get; }
public HeadlessBitmapStub(Size size, Vector dpi)
{
Size = size;
Dpi = dpi;
var pixel = Size * (Dpi / 96);
PixelSize = new PixelSize(Math.Max(1, (int)pixel.Width), Math.Max(1, (int)pixel.Height));
}
public HeadlessBitmapStub(PixelSize size, Vector dpi)
{
PixelSize = size;
Dpi = dpi;
Size = PixelSize.ToSizeWithDpi(dpi);
}
public void Dispose()
{
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return new HeadlessDrawingContextStub();
}
public void Blit(IDrawingContextImpl context)
{
}
public bool CanBlit => false;
public Vector Dpi { get; }
public PixelSize PixelSize { get; }
public int Version { get; set; }
public void Save(string fileName)
{
}
public void Save(Stream stream)
{
}
public ILockedFramebuffer Lock()
{
Version++;
var mem = Marshal.AllocHGlobal(PixelSize.Width * PixelSize.Height * 4);
return new LockedFramebuffer(mem, PixelSize, PixelSize.Width * 4, Dpi, PixelFormat.Rgba8888,
() => Marshal.FreeHGlobal(mem));
}
}
class HeadlessDrawingContextStub : IDrawingContextImpl
{
public void Dispose()
{
}
public Matrix Transform { get; set; }
public void Clear(Color color)
{
}
public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
{
}
public IDrawingContextLayerImpl CreateLayer(Size size)
{
return new HeadlessBitmapStub(size, new Vector(96, 96));
}
public void PushClip(Rect clip)
{
}
public void PopClip()
{
}
public void PushOpacity(double opacity)
{
}
public void PopOpacity()
{
}
public void PushOpacityMask(IBrush mask, Rect bounds)
{
}
public void PopOpacityMask()
{
}
public void PushGeometryClip(IGeometryImpl clip)
{
}
public void PopGeometryClip()
{
}
public void PushBitmapBlendMode(BitmapBlendingMode blendingMode)
{
}
public void PopBitmapBlendMode()
{
}
public void Custom(ICustomDrawOperation custom)
{
}
public void DrawLine(IPen pen, Point p1, Point p2)
{
throw new NotImplementedException();
}
public void DrawGeometry(IBrush brush, IPen pen, IGeometryImpl geometry)
{
}
public void DrawRectangle(IPen pen, Rect rect, float cornerRadius = 0)
{
}
public void DrawBitmap(IRef<IBitmapImpl> source, double opacity, Rect sourceRect, Rect destRect, BitmapInterpolationMode bitmapInterpolationMode = BitmapInterpolationMode.Default)
{
}
public void DrawBitmap(IRef<IBitmapImpl> source, IBrush opacityMask, Rect opacityMaskRect, Rect destRect)
{
}
public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadow = default)
{
}
public void DrawGlyphRun(IBrush foreground, GlyphRun glyphRun)
{
}
public void PushClip(RoundedRect clip)
{
}
}
class HeadlessRenderTarget : IRenderTarget
{
public void Dispose()
{
}
public IDrawingContextImpl CreateDrawingContext(IVisualBrushRenderer visualBrushRenderer)
{
return new HeadlessDrawingContextStub();
}
}
class HeadlessFormattedTextStub : IFormattedTextImpl
{
public HeadlessFormattedTextStub(string text, Size constraint)
{
Text = text;
Constraint = constraint;
Bounds = new Rect(Constraint.Constrain(new Size(50, 50)));
}
public Size Constraint { get; }
public Rect Bounds { get; }
public string Text { get; }
public IEnumerable<FormattedTextLine> GetLines()
{
return new[] { new FormattedTextLine(Text.Length, 10) };
}
public TextHitTestResult HitTestPoint(Point point) => new TextHitTestResult();
public Rect HitTestTextPosition(int index) => new Rect();
public IEnumerable<Rect> HitTestTextRange(int index, int length) => new Rect[length];
}
}
}