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.
 
 
 

294 lines
9.3 KiB

// "Therefore those skilled at the unorthodox
// are infinite as heaven and earth,
// inexhaustible as the great rivers.
// When they come to an end,
// they begin again,
// like the days and months;
// they die and are reborn,
// like the four seasons."
//
// - Sun Tsu,
// "The Art of War"
using System;
using System.Collections.Generic;
using System.Globalization;
using Perspex;
using Perspex.Media;
using TheArtOfDev.HtmlRenderer.Adapters;
using TheArtOfDev.HtmlRenderer.Adapters.Entities;
using TheArtOfDev.HtmlRenderer.Core.Utils;
using TheArtOfDev.HtmlRenderer.Perspex.Utilities;
namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
{
/// <summary>
/// Adapter for Perspex Graphics.
/// </summary>
internal sealed class GraphicsAdapter : RGraphics
{
#region Fields and Consts
/// <summary>
/// The wrapped Perspex graphics object
/// </summary>
private readonly IDrawingContext _g;
/// <summary>
/// if to release the graphics object on dispose
/// </summary>
private readonly bool _releaseGraphics;
#endregion
private Stack<IDisposable> _clipStack = new Stack<IDisposable>();
/// <summary>
/// Init.
/// </summary>
/// <param name="g">the Perspex graphics object to use</param>
/// <param name="initialClip">the initial clip of the graphics</param>
/// <param name="releaseGraphics">optional: if to release the graphics object on dispose (default - false)</param>
public GraphicsAdapter(IDrawingContext g, RRect initialClip, bool releaseGraphics = false)
: base(PerspexAdapter.Instance, initialClip)
{
ArgChecker.AssertArgNotNull(g, "g");
_g = g;
_releaseGraphics = releaseGraphics;
}
/// <summary>
/// Init.
/// </summary>
public GraphicsAdapter()
: base(PerspexAdapter.Instance, RRect.Empty)
{
_g = null;
_releaseGraphics = false;
}
public override void PopClip()
{
_clipStack.Pop()?.Dispose();
}
public override void PushClip(RRect rect)
{
_clipStack.Push(_g.PushClip(Util.Convert(rect)));
//_clipStack.Push(rect);
//_g.PushClip(new RectangleGeometry(Utils.Convert(rect)));
}
public override void PushClipExclude(RRect rect)
{
_clipStack.Push(null);
//TODO: Implement exclude rect, see #128
//var geometry = new CombinedGeometry();
//geometry.Geometry1 = new RectangleGeometry(Utils.Convert(_clipStack.Peek()));
//geometry.Geometry2 = new RectangleGeometry(Utils.Convert(rect));
//geometry.GeometryCombineMode = GeometryCombineMode.Exclude;
//_clipStack.Push(_clipStack.Peek());
//_g.PushClip(geometry);
}
public override Object SetAntiAliasSmoothingMode()
{
return null;
}
public override void ReturnPreviousSmoothingMode(Object prevMode)
{ }
public override RSize MeasureString(string str, RFont font)
{
var text = GetText(str, font);
var measure = text.Measure();
return new RSize(measure.Width, measure.Height);
}
FormattedText GetText(string str, RFont font)
{
var f = ((FontAdapter)font);
return new FormattedText(str, f.Name, font.Size, f.FontStyle, TextAlignment.Left, f.Weight);
}
public override void MeasureString(string str, RFont font, double maxWidth, out int charFit, out double charFitWidth)
{
var text = GetText(str, font);
var fullLength = text.Measure().Width;
if (fullLength < maxWidth)
{
charFitWidth = fullLength;
charFit = str.Length;
return;
}
int lastLen = 0;
double lastMeasure = 0;
BinarySearch(len =>
{
text = GetText(str.Substring(0, len), font);
var size = text.Measure().Width;
lastMeasure = size;
lastLen = len;
if (size <= maxWidth)
return -1;
return 1;
}, 0, str.Length);
if (lastMeasure > maxWidth)
{
lastLen--;
lastMeasure = GetText(str.Substring(0, lastLen), font).Measure().Width;
}
charFit = lastLen;
charFitWidth = lastMeasure;
}
private static int BinarySearch(Func<int, int> condition, int start, int end)
{
do
{
int ind = start + (end - start)/2;
int res = condition(ind);
if (res == 0)
return ind;
else if (res > 0)
{
if (start != ind)
start = ind;
else
start = ind + 1;
}
else
end = ind;
} while (end > start);
return -1;
}
public override void DrawString(string str, RFont font, RColor color, RPoint point, RSize size, bool rtl)
{
var text = GetText(str, font);
text.Constraint = Util.Convert(size);
_g.DrawText(new SolidColorBrush(Util.Convert(color)), Util.Convert(point), text);
}
public override RBrush GetTextureBrush(RImage image, RRect dstRect, RPoint translateTransformLocation)
{
//TODO: Implement texture brush
return PerspexAdapter.Instance.GetSolidBrush(Util.Convert(Colors.Magenta));
//var brush = new ImageBrush(((ImageAdapter)image).Image);
//brush.Stretch = Stretch.None;
//brush.TileMode = TileMode.Tile;
//brush.Viewport = Utils.Convert(dstRect);
//brush.ViewportUnits = BrushMappingMode.Absolute;
//brush.Transform = new TranslateTransform(translateTransformLocation.X, translateTransformLocation.Y);
//brush.Freeze();
//return new BrushAdapter(brush);
}
public override RGraphicsPath GetGraphicsPath()
{
return new GraphicsPathAdapter();
}
public override void Dispose()
{
//TODO: Do something about Dispose
//if (_releaseGraphics)
// _g.Close();
}
#region Delegate graphics methods
public override void DrawLine(RPen pen, double x1, double y1, double x2, double y2)
{
x1 = (int)x1;
x2 = (int)x2;
y1 = (int)y1;
y2 = (int)y2;
var adj = pen.Width;
if (Math.Abs(x1 - x2) < .1 && Math.Abs(adj % 2 - 1) < .1)
{
x1 += .5;
x2 += .5;
}
if (Math.Abs(y1 - y2) < .1 && Math.Abs(adj % 2 - 1) < .1)
{
y1 += .5;
y2 += .5;
}
_g.DrawLine(((PenAdapter)pen).CreatePen(), new Point(x1, y1), new Point(x2, y2));
}
public override void DrawRectangle(RPen pen, double x, double y, double width, double height)
{
var adj = pen.Width;
if (Math.Abs(adj % 2 - 1) < .1)
{
x += .5;
y += .5;
}
_g.DrawRectange(((PenAdapter) pen).CreatePen(), new Rect(x, y, width, height));
}
public override void DrawRectangle(RBrush brush, double x, double y, double width, double height)
{
_g.FillRectange(((BrushAdapter) brush).Brush, new Rect(x, y, width, height));
}
public override void DrawImage(RImage image, RRect destRect, RRect srcRect)
{
_g.DrawImage(((ImageAdapter) image).Image, 1, Util.Convert(srcRect), Util.Convert(destRect));
}
public override void DrawImage(RImage image, RRect destRect)
{
_g.DrawImage(((ImageAdapter) image).Image, 1, new Rect(0, 0, image.Width, image.Height),
Util.Convert(destRect));
}
public override void DrawPath(RPen pen, RGraphicsPath path)
{
_g.DrawGeometry(null, ((PenAdapter)pen).CreatePen(), ((GraphicsPathAdapter)path).GetClosedGeometry());
}
public override void DrawPath(RBrush brush, RGraphicsPath path)
{
_g.DrawGeometry(((BrushAdapter)brush).Brush, null, ((GraphicsPathAdapter)path).GetClosedGeometry());
}
public override void DrawPolygon(RBrush brush, RPoint[] points)
{
if (points != null && points.Length > 0)
{
var g = new StreamGeometry();
using (var context = g.Open())
{
context.BeginFigure(Util.Convert(points[0]), true);
for (int i = 1; i < points.Length; i++)
context.LineTo(Util.Convert(points[i]));
context.EndFigure(false);
}
_g.DrawGeometry(((BrushAdapter)brush).Brush, null, g);
}
}
#endregion
}
}