| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394 |
- // Copyright (c) The Avalonia Project. All rights reserved.
- // Licensed under the MIT license. See licence.md file in the project root for full license information.
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using Avalonia.Media;
- using Avalonia.Platform;
- using SharpDX;
- using SharpDX.Direct2D1;
- using SharpDX.Mathematics.Interop;
- using IBitmap = Avalonia.Media.Imaging.IBitmap;
- namespace Avalonia.Direct2D1.Media
- {
- /// <summary>
- /// Draws using Direct2D1.
- /// </summary>
- public class DrawingContextImpl : IDrawingContextImpl, IDisposable
- {
- /// <summary>
- /// The Direct2D1 render target.
- /// </summary>
- private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
- /// <summary>
- /// The DirectWrite factory.
- /// </summary>
- private SharpDX.DirectWrite.Factory _directWriteFactory;
- private SharpDX.DXGI.SwapChain1 _swapChain;
- /// <summary>
- /// Initializes a new instance of the <see cref="DrawingContextImpl"/> class.
- /// </summary>
- /// <param name="renderTarget">The render target to draw to.</param>
- /// <param name="directWriteFactory">The DirectWrite factory.</param>
- /// <param name="swapChain">An optional swap chain associated with this drawing context.</param>
- public DrawingContextImpl(
- SharpDX.Direct2D1.RenderTarget renderTarget,
- SharpDX.DirectWrite.Factory directWriteFactory,
- SharpDX.DXGI.SwapChain1 swapChain = null)
- {
- _renderTarget = renderTarget;
- _directWriteFactory = directWriteFactory;
- _swapChain = swapChain;
- _renderTarget.BeginDraw();
- }
- /// <summary>
- /// Gets the current transform of the drawing context.
- /// </summary>
- public Matrix Transform
- {
- get { return _renderTarget.Transform.ToAvalonia(); }
- set { _renderTarget.Transform = value.ToDirect2D(); }
- }
- /// <inheritdoc/>
- public void Clear(Color color)
- {
- _renderTarget.Clear(color.ToDirect2D());
- }
- /// <summary>
- /// Ends a draw operation.
- /// </summary>
- public void Dispose()
- {
- foreach (var layer in _layerPool)
- layer.Dispose();
- try
- {
- _renderTarget.EndDraw();
-
- _swapChain?.Present(1, SharpDX.DXGI.PresentFlags.None);
- }
- catch (SharpDXException ex) when((uint)ex.HResult == 0x8899000C) // D2DERR_RECREATE_TARGET
- {
- throw new RenderTargetCorruptedException(ex);
- }
- }
- /// <summary>
- /// Draws a bitmap image.
- /// </summary>
- /// <param name="source">The bitmap image.</param>
- /// <param name="opacity">The opacity to draw with.</param>
- /// <param name="sourceRect">The rect in the image to draw.</param>
- /// <param name="destRect">The rect in the output to draw to.</param>
- public void DrawImage(IBitmapImpl source, double opacity, Rect sourceRect, Rect destRect)
- {
- var impl = (BitmapImpl)source;
- Bitmap d2d = impl.GetDirect2DBitmap(_renderTarget);
- _renderTarget.DrawBitmap(
- d2d,
- destRect.ToSharpDX(),
- (float)opacity,
- BitmapInterpolationMode.Linear,
- sourceRect.ToSharpDX());
- }
- /// <summary>
- /// Draws a line.
- /// </summary>
- /// <param name="pen">The stroke pen.</param>
- /// <param name="p1">The first point of the line.</param>
- /// <param name="p2">The second point of the line.</param>
- public void DrawLine(Pen pen, Point p1, Point p2)
- {
- if (pen != null)
- {
- var size = new Rect(p1, p2).Size;
- using (var d2dBrush = CreateBrush(pen.Brush, size))
- using (var d2dStroke = pen.ToDirect2DStrokeStyle(_renderTarget))
- {
- if (d2dBrush.PlatformBrush != null)
- {
- _renderTarget.DrawLine(
- p1.ToSharpDX(),
- p2.ToSharpDX(),
- d2dBrush.PlatformBrush,
- (float)pen.Thickness,
- d2dStroke);
- }
- }
- }
- }
- /// <summary>
- /// Draws a geometry.
- /// </summary>
- /// <param name="brush">The fill brush.</param>
- /// <param name="pen">The stroke pen.</param>
- /// <param name="geometry">The geometry.</param>
- public void DrawGeometry(IBrush brush, Pen pen, IGeometryImpl geometry)
- {
- if (brush != null)
- {
- using (var d2dBrush = CreateBrush(brush, geometry.Bounds.Size))
- {
- if (d2dBrush.PlatformBrush != null)
- {
- var impl = (GeometryImpl)geometry;
- _renderTarget.FillGeometry(impl.Geometry, d2dBrush.PlatformBrush);
- }
- }
- }
- if (pen != null)
- {
- using (var d2dBrush = CreateBrush(pen.Brush, geometry.GetRenderBounds(pen.Thickness).Size))
- using (var d2dStroke = pen.ToDirect2DStrokeStyle(_renderTarget))
- {
- if (d2dBrush.PlatformBrush != null)
- {
- var impl = (GeometryImpl)geometry;
- _renderTarget.DrawGeometry(impl.Geometry, d2dBrush.PlatformBrush, (float)pen.Thickness, d2dStroke);
- }
- }
- }
- }
- /// <summary>
- /// Draws the outline of a rectangle.
- /// </summary>
- /// <param name="pen">The pen.</param>
- /// <param name="rect">The rectangle bounds.</param>
- /// <param name="cornerRadius">The corner radius.</param>
- public void DrawRectangle(Pen pen, Rect rect, float cornerRadius)
- {
- using (var brush = CreateBrush(pen.Brush, rect.Size))
- using (var d2dStroke = pen.ToDirect2DStrokeStyle(_renderTarget))
- {
- if (brush.PlatformBrush != null)
- {
- if (cornerRadius == 0)
- {
- _renderTarget.DrawRectangle(
- rect.ToDirect2D(),
- brush.PlatformBrush,
- (float)pen.Thickness,
- d2dStroke);
- }
- else
- {
- _renderTarget.DrawRoundedRectangle(
- new RoundedRectangle { Rect = rect.ToDirect2D(), RadiusX = cornerRadius, RadiusY = cornerRadius },
- brush.PlatformBrush,
- (float)pen.Thickness,
- d2dStroke);
- }
- }
- }
- }
- /// <summary>
- /// Draws text.
- /// </summary>
- /// <param name="foreground">The foreground brush.</param>
- /// <param name="origin">The upper-left corner of the text.</param>
- /// <param name="text">The text.</param>
- public void DrawText(IBrush foreground, Point origin, IFormattedTextImpl text)
- {
- if (!string.IsNullOrEmpty(text.Text))
- {
- var impl = (FormattedTextImpl)text;
- using (var brush = CreateBrush(foreground, impl.Size))
- using (var renderer = new AvaloniaTextRenderer(this, _renderTarget, brush.PlatformBrush))
- {
- if (brush.PlatformBrush != null)
- {
- impl.TextLayout.Draw(renderer, (float)origin.X, (float)origin.Y);
- }
- }
- }
- }
- /// <summary>
- /// Draws a filled rectangle.
- /// </summary>
- /// <param name="brush">The brush.</param>
- /// <param name="rect">The rectangle bounds.</param>
- /// <param name="cornerRadius">The corner radius.</param>
- public void FillRectangle(IBrush brush, Rect rect, float cornerRadius)
- {
- using (var b = CreateBrush(brush, rect.Size))
- {
- if (b.PlatformBrush != null)
- {
- if (cornerRadius == 0)
- {
- _renderTarget.FillRectangle(rect.ToDirect2D(), b.PlatformBrush);
- }
- else
- {
- _renderTarget.FillRoundedRectangle(
- new RoundedRectangle
- {
- Rect = new RawRectangleF(
- (float)rect.X,
- (float)rect.Y,
- (float)rect.Right,
- (float)rect.Bottom),
- RadiusX = cornerRadius,
- RadiusY = cornerRadius
- },
- b.PlatformBrush);
- }
- }
- }
- }
- /// <summary>
- /// Pushes a clip rectange.
- /// </summary>
- /// <param name="clip">The clip rectangle.</param>
- /// <returns>A disposable used to undo the clip rectangle.</returns>
- public void PushClip(Rect clip)
- {
- _renderTarget.PushAxisAlignedClip(clip.ToSharpDX(), AntialiasMode.PerPrimitive);
- }
- public void PopClip()
- {
- _renderTarget.PopAxisAlignedClip();
- }
- readonly Stack<Layer> _layers = new Stack<Layer>();
- private readonly Stack<Layer> _layerPool = new Stack<Layer>();
- /// <summary>
- /// Pushes an opacity value.
- /// </summary>
- /// <param name="opacity">The opacity.</param>
- /// <returns>A disposable used to undo the opacity.</returns>
- public void PushOpacity(double opacity)
- {
- if (opacity < 1)
- {
- var parameters = new LayerParameters
- {
- ContentBounds = PrimitiveExtensions.RectangleInfinite,
- MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
- Opacity = (float) opacity,
- };
- var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget);
- _renderTarget.PushLayer(ref parameters, layer);
- _layers.Push(layer);
- }
- else
- _layers.Push(null);
- }
- public void PopOpacity()
- {
- PopLayer();
- }
- private void PopLayer()
- {
- var layer = _layers.Pop();
- if (layer != null)
- {
- _renderTarget.PopLayer();
- _layerPool.Push(layer);
- }
- }
- /// <summary>
- /// Creates a Direct2D brush wrapper for a Avalonia brush.
- /// </summary>
- /// <param name="brush">The avalonia brush.</param>
- /// <param name="destinationSize">The size of the brush's target area.</param>
- /// <returns>The Direct2D brush wrapper.</returns>
- public BrushImpl CreateBrush(IBrush brush, Size destinationSize)
- {
- var solidColorBrush = brush as ISolidColorBrush;
- var linearGradientBrush = brush as ILinearGradientBrush;
- var radialGradientBrush = brush as IRadialGradientBrush;
- var imageBrush = brush as IImageBrush;
- var visualBrush = brush as IVisualBrush;
- if (solidColorBrush != null)
- {
- return new SolidColorBrushImpl(solidColorBrush, _renderTarget);
- }
- else if (linearGradientBrush != null)
- {
- return new LinearGradientBrushImpl(linearGradientBrush, _renderTarget, destinationSize);
- }
- else if (radialGradientBrush != null)
- {
- return new RadialGradientBrushImpl(radialGradientBrush, _renderTarget, destinationSize);
- }
- else if (imageBrush != null)
- {
- return new TileBrushImpl(imageBrush, _renderTarget, destinationSize);
- }
- else if (visualBrush != null)
- {
- return new TileBrushImpl(visualBrush, _renderTarget, destinationSize);
- }
- else
- {
- return new SolidColorBrushImpl(null, _renderTarget);
- }
- }
- public void PushGeometryClip(IGeometryImpl clip)
- {
- var parameters = new LayerParameters
- {
- ContentBounds = PrimitiveExtensions.RectangleInfinite,
- MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
- Opacity = 1,
- GeometricMask = ((GeometryImpl)clip).Geometry
- };
- var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget);
- _renderTarget.PushLayer(ref parameters, layer);
- _layers.Push(layer);
- }
- public void PopGeometryClip()
- {
- PopLayer();
- }
- public void PushOpacityMask(IBrush mask, Rect bounds)
- {
- var parameters = new LayerParameters
- {
- ContentBounds = PrimitiveExtensions.RectangleInfinite,
- MaskTransform = PrimitiveExtensions.Matrix3x2Identity,
- Opacity = 1,
- OpacityBrush = CreateBrush(mask, bounds.Size).PlatformBrush
- };
- var layer = _layerPool.Count != 0 ? _layerPool.Pop() : new Layer(_renderTarget);
- _renderTarget.PushLayer(ref parameters, layer);
- _layers.Push(layer);
- }
- public void PopOpacityMask()
- {
- PopLayer();
- }
- }
- }
|