// -----------------------------------------------------------------------
//
// Copyright 2013 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
namespace Perspex.Direct2D1.Media
{
using System;
using System.Reactive.Disposables;
using Perspex.Media;
using SharpDX;
using SharpDX.Direct2D1;
using IBitmap = Perspex.Media.Imaging.IBitmap;
using Matrix = Perspex.Matrix;
///
/// Draws using Direct2D1.
///
public class DrawingContext : IDrawingContext, IDisposable
{
///
/// The Direct2D1 render target.
///
private RenderTarget renderTarget;
///
/// The DirectWrite factory.
///
private SharpDX.DirectWrite.Factory directWriteFactory;
///
/// Initializes a new instance of the class.
///
/// The render target to draw to.
/// The DirectWrite factory.
public DrawingContext(
RenderTarget renderTarget,
SharpDX.DirectWrite.Factory directWriteFactory)
{
this.renderTarget = renderTarget;
this.directWriteFactory = directWriteFactory;
this.renderTarget.BeginDraw();
}
public Matrix CurrentTransform
{
get { return this.renderTarget.Transform.ToPerspex(); }
set { this.renderTarget.Transform = value.ToDirect2D(); }
}
///
/// Ends a draw operation.
///
public void Dispose()
{
this.renderTarget.EndDraw();
}
public void DrawImage(IBitmap bitmap, double opacity, Rect sourceRect, Rect destRect)
{
BitmapImpl impl = (BitmapImpl)bitmap.PlatformImpl;
Bitmap d2d = impl.GetDirect2DBitmap(this.renderTarget);
this.renderTarget.DrawBitmap(
d2d,
destRect.ToSharpDX(),
(float)opacity,
BitmapInterpolationMode.Linear,
sourceRect.ToSharpDX());
}
///
/// Draws a line.
///
/// The stroke pen.
/// The first point of the line.
/// The second point of the line.
public void DrawLine(Pen pen, Perspex.Point p1, Perspex.Point p2)
{
if (pen != null)
{
using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
{
this.renderTarget.DrawLine(
p1.ToSharpDX(),
p2.ToSharpDX(),
d2dBrush,
(float)pen.Thickness,
d2dStroke);
}
}
}
///
/// Draws a geometry.
///
/// The fill brush.
/// The stroke pen.
/// The geometry.
public void DrawGeometry(Perspex.Media.Brush brush, Perspex.Media.Pen pen, Perspex.Media.Geometry geometry)
{
if (brush != null)
{
using (var d2dBrush = brush.ToDirect2D(this.renderTarget))
{
GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
this.renderTarget.FillGeometry(impl.Geometry, d2dBrush);
}
}
if (pen != null)
{
using (var d2dBrush = pen.Brush.ToDirect2D(this.renderTarget))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
{
GeometryImpl impl = (GeometryImpl)geometry.PlatformImpl;
this.renderTarget.DrawGeometry(impl.Geometry, d2dBrush, (float)pen.Thickness, d2dStroke);
}
}
}
///
/// Draws the outline of a rectangle.
///
/// The pen.
/// The rectangle bounds.
public void DrawRectange(Pen pen, Rect rect, float cornerRadius)
{
using (var brush = pen.Brush.ToDirect2D(this.renderTarget))
using (var d2dStroke = pen.ToDirect2DStrokeStyle(this.renderTarget))
{
this.renderTarget.DrawRoundedRectangle(
new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
brush,
(float)pen.Thickness,
d2dStroke);
}
}
///
/// Draws text.
///
/// The foreground brush.
/// The upper-left corner of the text.
/// The text.
public void DrawText(Perspex.Media.Brush foreground, Perspex.Point origin, FormattedText text)
{
if (!string.IsNullOrEmpty(text.Text))
{
var impl = (FormattedTextImpl)text.PlatformImpl;
using (var renderer = new PerspexTextRenderer(this.renderTarget, foreground.ToDirect2D(this.renderTarget)))
{
impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y);
}
}
}
///
/// Draws a filled rectangle.
///
/// The brush.
/// The rectangle bounds.
public void FillRectange(Perspex.Media.Brush brush, Rect rect, float cornerRadius)
{
using (var b = brush.ToDirect2D(this.renderTarget))
{
this.renderTarget.FillRoundedRectangle(
new RoundedRectangle
{
Rect = new RectangleF(
(float)rect.X,
(float)rect.Y,
(float)rect.Width,
(float)rect.Height),
RadiusX = cornerRadius,
RadiusY = cornerRadius
},
b);
}
}
///
/// Pushes a clip rectange.
///
/// The clip rectangle.
/// A disposable used to undo the clip rectangle.
public IDisposable PushClip(Rect clip)
{
this.renderTarget.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive);
return Disposable.Create(() =>
{
this.renderTarget.PopAxisAlignedClip();
});
}
///
/// Pushes an opacity value.
///
/// The opacity.
/// A disposable used to undo the opacity.
public IDisposable PushOpacity(double opacity)
{
if (opacity < 1)
{
var parameters = new LayerParameters
{
ContentBounds = RectangleF.Infinite,
MaskTransform = Matrix3x2.Identity,
Opacity = (float)opacity,
};
var layer = new Layer(this.renderTarget);
this.renderTarget.PushLayer(ref parameters, layer);
return Disposable.Create(() =>
{
this.renderTarget.PopLayer();
});
}
else
{
return Disposable.Empty;
}
}
///
/// Pushes a matrix transformation.
///
/// The matrix
/// A disposable used to undo the transformation.
public IDisposable PushTransform(Matrix matrix)
{
Matrix3x2 m3x2 = matrix.ToDirect2D();
Matrix3x2 transform = this.renderTarget.Transform * m3x2;
this.renderTarget.Transform = transform;
return Disposable.Create(() =>
{
m3x2.Invert();
this.renderTarget.Transform = transform * m3x2;
});
}
}
}