diff --git a/src/Gtk/Perspex.Cairo/Media/BrushImpl.cs b/src/Gtk/Perspex.Cairo/Media/BrushImpl.cs new file mode 100644 index 0000000000..76ad34b19a --- /dev/null +++ b/src/Gtk/Perspex.Cairo/Media/BrushImpl.cs @@ -0,0 +1,17 @@ +using System; +using global::Cairo; + +namespace Perspex.Cairo +{ + public abstract class BrushImpl : IDisposable + { + public Pattern PlatformBrush { get; protected set; } + + public void Dispose() + { + if (this.PlatformBrush != null) + this.PlatformBrush.Dispose(); + } + } +} + diff --git a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs index 0bf21e31af..31ab48f2c0 100644 --- a/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs +++ b/src/Gtk/Perspex.Cairo/Media/DrawingContext.cs @@ -95,11 +95,12 @@ namespace Perspex.Cairo.Media { var size = new Rect(p1, p2).Size; - SetPen(pen, size); - - _context.MoveTo(p1.ToCairo()); - _context.LineTo(p2.ToCairo()); - _context.Stroke(); + using (var p = SetPen(pen, size)) + { + _context.MoveTo(p1.ToCairo()); + _context.LineTo(p2.ToCairo()); + _context.Stroke(); + } } /// @@ -118,19 +119,22 @@ namespace Perspex.Cairo.Media if (brush != null) { - SetBrush(brush, geometry.Bounds.Size); - - if (pen != null) - _context.FillPreserve(); - else - _context.Fill(); + using (var b = SetBrush(brush, geometry.Bounds.Size)) + { + if (pen != null) + _context.FillPreserve(); + else + _context.Fill(); + } } } if (pen != null) { - SetPen(pen, geometry.Bounds.Size); - _context.Stroke(); + using (var p = SetPen(pen, geometry.Bounds.Size)) + { + _context.Stroke(); + } } } @@ -141,9 +145,11 @@ namespace Perspex.Cairo.Media /// The rectangle bounds. public void DrawRectange(Pen pen, Rect rect, float cornerRadius) { - SetPen(pen, rect.Size); - _context.Rectangle(rect.ToCairo()); - _context.Stroke(); + using (var p = SetPen(pen, rect.Size)) + { + _context.Rectangle(rect.ToCairo ()); + _context.Stroke(); + } } /// @@ -157,8 +163,10 @@ namespace Perspex.Cairo.Media var layout = ((FormattedTextImpl)text.PlatformImpl).Layout; _context.MoveTo(origin.X, origin.Y); - SetBrush(foreground, new Size(0, 0)); - Pango.CairoHelper.ShowLayout(_context, layout); + using (var b = SetBrush(foreground, new Size(0, 0))) + { + Pango.CairoHelper.ShowLayout(_context, layout); + } } /// @@ -168,9 +176,11 @@ namespace Perspex.Cairo.Media /// The rectangle bounds. public void FillRectange(Brush brush, Rect rect, float cornerRadius) { - SetBrush(brush, rect.Size); - _context.Rectangle(rect.ToCairo()); - _context.Fill(); + using (var b = SetBrush(brush, rect.Size)) + { + _context.Rectangle(rect.ToCairo ()); + _context.Fill(); + } } /// @@ -212,48 +222,44 @@ namespace Perspex.Cairo.Media }); } - private void SetBrush(Brush brush, Size destinationSize) + private double opacityOverride = 1.0f; + + private BrushImpl SetBrush(Brush brush, Size destinationSize) { var solid = brush as SolidColorBrush; var linearGradientBrush = brush as LinearGradientBrush; var imageBrush = brush as ImageBrush; var visualBrush = brush as VisualBrush; - - if (solid != null) - { - _context.SetSourceRGBA( - solid.Color.R / 255.0, - solid.Color.G / 255.0, - solid.Color.B / 255.0, - solid.Color.A / 255.0); - } - else if (linearGradientBrush != null) - { - var start = linearGradientBrush.StartPoint.ToPixels(destinationSize); - var end = linearGradientBrush.EndPoint.ToPixels(destinationSize); - - Cairo.LinearGradient g = new Cairo.LinearGradient(start.X, start.Y, end.X, end.Y); - - foreach (var s in linearGradientBrush.GradientStops) - g.AddColorStop(s.Offset, s.Color.ToCairo()); - - g.Extend = Cairo.Extend.Pad; - - _context.SetSource(g); - } - else if (imageBrush != null) - { - _context.SetSource(TileBrushes.CreateImageBrush(imageBrush, destinationSize)); - } - else if (visualBrush != null) - { - _context.SetSource(TileBrushes.CreateVisualBrush(visualBrush, destinationSize)); - } + BrushImpl impl = null; + + if (solid != null) + { + impl = new SolidColorBrushImpl(solid, opacityOverride); + } + else if (linearGradientBrush != null) + { + impl = new LinearGradientBrushImpl(linearGradientBrush, destinationSize); + } + else if (imageBrush != null) + { + impl = new ImageBrushImpl(imageBrush, destinationSize); + } + else if (visualBrush != null) + { + impl = new VisualBrushImpl(visualBrush, destinationSize); + } + else + { + impl = new SolidColorBrushImpl(null, 1.0f); + } + + _context.SetSource(impl.PlatformBrush); + + return impl; } - private void SetPen(Pen pen, Size destinationSize) + private BrushImpl SetPen(Pen pen, Size destinationSize) { - SetBrush(pen.Brush, destinationSize); if (pen.DashStyle != null) { if (pen.DashStyle.Dashes != null && pen.DashStyle.Dashes.Count > 0) @@ -271,6 +277,8 @@ namespace Perspex.Cairo.Media // TODO: Figure out a solution for this. _context.LineJoin = Cairo.LineJoin.Miter; _context.LineCap = Cairo.LineCap.Butt; + + return SetBrush(pen.Brush, destinationSize); } } } diff --git a/src/Gtk/Perspex.Cairo/Media/ImageBrushImpl.cs b/src/Gtk/Perspex.Cairo/Media/ImageBrushImpl.cs new file mode 100644 index 0000000000..d6c781619c --- /dev/null +++ b/src/Gtk/Perspex.Cairo/Media/ImageBrushImpl.cs @@ -0,0 +1,14 @@ +using System; +using global::Cairo; + +namespace Perspex.Cairo.Media +{ + public class ImageBrushImpl : BrushImpl + { + public ImageBrushImpl(Perspex.Media.ImageBrush brush, Size destinationSize) + { + this.PlatformBrush = TileBrushes.CreateImageBrush(brush, destinationSize); + } + } +} + diff --git a/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs b/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs index ef816d6910..db35a22775 100644 --- a/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs +++ b/src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs @@ -13,6 +13,7 @@ namespace Perspex.Cairo.Media.Imaging public RenderTargetBitmapImpl(Cairo.ImageSurface surface) { Surface = surface; + renderer = new Renderer(Surface); } public int PixelWidth => Surface.Width; @@ -21,7 +22,7 @@ namespace Perspex.Cairo.Media.Imaging public void Dispose() { - Surface.Dispose(); + renderer.Dispose(); } public Cairo.ImageSurface Surface @@ -29,9 +30,9 @@ namespace Perspex.Cairo.Media.Imaging get; } + private Renderer renderer; public void Render(IVisual visual) { - Renderer renderer = new Renderer(Surface); renderer.Render(visual, new PlatformHandle(IntPtr.Zero, "RTB")); } diff --git a/src/Gtk/Perspex.Cairo/Media/LinearGradientBrushImpl.cs b/src/Gtk/Perspex.Cairo/Media/LinearGradientBrushImpl.cs new file mode 100644 index 0000000000..f1502275c7 --- /dev/null +++ b/src/Gtk/Perspex.Cairo/Media/LinearGradientBrushImpl.cs @@ -0,0 +1,22 @@ +using System; +using global::Cairo; + +namespace Perspex.Cairo +{ + public class LinearGradientBrushImpl : BrushImpl + { + public LinearGradientBrushImpl(Perspex.Media.LinearGradientBrush brush, Size destinationSize) + { + var start = brush.StartPoint.ToPixels(destinationSize); + var end = brush.EndPoint.ToPixels(destinationSize); + + this.PlatformBrush = new LinearGradient(start.X, start.Y, end.X, end.Y); + + foreach (var stop in brush.GradientStops) + ((LinearGradient)this.PlatformBrush).AddColorStop(stop.Offset, stop.Color.ToCairo()); + + ((LinearGradient)this.PlatformBrush).Extend = Extend.Pad; + } + } +} + diff --git a/src/Gtk/Perspex.Cairo/Media/SolidColorBrushImpl.cs b/src/Gtk/Perspex.Cairo/Media/SolidColorBrushImpl.cs new file mode 100644 index 0000000000..53db187a0a --- /dev/null +++ b/src/Gtk/Perspex.Cairo/Media/SolidColorBrushImpl.cs @@ -0,0 +1,20 @@ +using System; +using global::Cairo; + +namespace Perspex.Cairo +{ + public class SolidColorBrushImpl : BrushImpl + { + public SolidColorBrushImpl(Perspex.Media.SolidColorBrush brush, double opacityOverride = 1.0f) + { + var color = brush?.Color.ToCairo() ?? new Color(); + + if (brush != null && brush.Opacity > 1) + color.A = Math.Min(brush.Opacity, color.A); + + color.A = Math.Min(opacityOverride, color.A); + this.PlatformBrush = new SolidPattern(brush?.Color.ToCairo() ?? new Color()); + } + } +} + diff --git a/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs b/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs index 016954030f..026729f26f 100644 --- a/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs +++ b/src/Gtk/Perspex.Cairo/Media/TileBrushes.cs @@ -29,8 +29,8 @@ namespace Perspex.Cairo.Media var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); - var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height); + using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height)) using (var context = new Context(intermediate)) { Rect drawRect; @@ -98,8 +98,8 @@ namespace Perspex.Cairo.Media var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size); var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale); var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size); - var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height); - + + using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height)) using (var context = new Context(intermediate)) { Rect drawRect; diff --git a/src/Gtk/Perspex.Cairo/Media/VisualBrushImpl.cs b/src/Gtk/Perspex.Cairo/Media/VisualBrushImpl.cs new file mode 100644 index 0000000000..634b2b38a5 --- /dev/null +++ b/src/Gtk/Perspex.Cairo/Media/VisualBrushImpl.cs @@ -0,0 +1,14 @@ +using System; +using global::Cairo; + +namespace Perspex.Cairo.Media +{ + public class VisualBrushImpl : BrushImpl + { + public VisualBrushImpl(Perspex.Media.VisualBrush brush, Size destinationSize) + { + this.PlatformBrush = TileBrushes.CreateVisualBrush(brush, destinationSize); + } + } +} + diff --git a/src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj b/src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj index 2ce19eb890..7d3aef2ec0 100644 --- a/src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj +++ b/src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj @@ -32,18 +32,10 @@ 4 - - gtk-sharp-2.0 - - - glib-sharp-2.0 - - - gtk-sharp-2.0 - - - gtk-sharp-2.0 - + + + + @@ -58,9 +50,7 @@ ..\..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll - - gtk-sharp-2.0 - + ..\..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll @@ -77,6 +67,11 @@ + + + + + diff --git a/src/Gtk/Perspex.Cairo/Renderer.cs b/src/Gtk/Perspex.Cairo/Renderer.cs index 9454ce4bea..13bb63445f 100644 --- a/src/Gtk/Perspex.Cairo/Renderer.cs +++ b/src/Gtk/Perspex.Cairo/Renderer.cs @@ -74,6 +74,8 @@ namespace Perspex.Cairo public override void Dispose() { + if (_surface != null) + _surface.Dispose(); } } } diff --git a/src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj b/src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj index 26003fc6ca..c3e15c371d 100644 --- a/src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj +++ b/src/Gtk/Perspex.Gtk/Perspex.Gtk.csproj @@ -31,15 +31,9 @@ - - gtk-sharp-2.0 - - - gtk-sharp-2.0 - - - gtk-sharp-2.0 - + + + ..\..\..\packages\Rx-Interfaces.2.2.5\lib\net45\System.Reactive.Interfaces.dll @@ -49,9 +43,7 @@ ..\..\..\packages\Rx-Linq.2.2.5\lib\net45\System.Reactive.Linq.dll - - glib-sharp-2.0 - + ..\..\..\packages\Splat.1.6.2\lib\Net45\Splat.dll diff --git a/tests/Perspex.RenderTests/TestBase.cs b/tests/Perspex.RenderTests/TestBase.cs index 18e406fedd..2940c5ab31 100644 Binary files a/tests/Perspex.RenderTests/TestBase.cs and b/tests/Perspex.RenderTests/TestBase.cs differ