|
|
@@ -8,115 +8,22 @@ using Perspex.Layout;
|
|
|
using Perspex.Media;
|
|
|
using Perspex.Platform;
|
|
|
using Perspex.Rendering;
|
|
|
+using Perspex.RenderHelpers;
|
|
|
|
|
|
namespace Perspex.Cairo.Media
|
|
|
{
|
|
|
internal static class TileBrushes
|
|
|
{
|
|
|
- public static SurfacePattern CreateImageBrush(ImageBrush brush, Size targetSize)
|
|
|
+ public static SurfacePattern CreateTileBrush(TileBrush brush, Size targetSize)
|
|
|
{
|
|
|
- if (brush.Source == null)
|
|
|
- {
|
|
|
+ var helper = new TileBrushImplHelper(brush, targetSize);
|
|
|
+ if (!helper.IsValid)
|
|
|
return null;
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: This is directly ported from Direct2D and could probably be made more
|
|
|
- // efficient on cairo by taking advantage of the fact that cairo has Extend.None.
|
|
|
- var image = ((BitmapImpl)brush.Source.PlatformImpl).Surface;
|
|
|
- var imageSize = new Size(brush.Source.PixelWidth, brush.Source.PixelHeight);
|
|
|
- var tileMode = brush.TileMode;
|
|
|
- var sourceRect = brush.SourceRect.ToPixels(imageSize);
|
|
|
- var destinationRect = brush.DestinationRect.ToPixels(targetSize);
|
|
|
- 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 context = new Context(intermediate))
|
|
|
- {
|
|
|
- Rect drawRect;
|
|
|
- var transform = CalculateIntermediateTransform(
|
|
|
- tileMode,
|
|
|
- sourceRect,
|
|
|
- destinationRect,
|
|
|
- scale,
|
|
|
- translate,
|
|
|
- out drawRect);
|
|
|
- context.Rectangle(drawRect.ToCairo());
|
|
|
- context.Clip();
|
|
|
- context.Transform(transform.ToCairo());
|
|
|
- Gdk.CairoHelper.SetSourcePixbuf(context, image, 0, 0);
|
|
|
- context.Rectangle(0, 0, imageSize.Width, imageSize.Height);
|
|
|
- context.Fill();
|
|
|
-
|
|
|
- var result = new SurfacePattern(intermediate);
|
|
|
-
|
|
|
- if ((brush.TileMode & TileMode.FlipXY) != 0)
|
|
|
- {
|
|
|
- // TODO: Currently always FlipXY as that's all cairo supports natively.
|
|
|
- // Support separate FlipX and FlipY by drawing flipped images to intermediate
|
|
|
- // surface.
|
|
|
- result.Extend = Extend.Reflect;
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- result.Extend = Extend.Repeat;
|
|
|
- }
|
|
|
-
|
|
|
- if (brush.TileMode != TileMode.None)
|
|
|
- {
|
|
|
- var matrix = result.Matrix;
|
|
|
- matrix.InitTranslate(-destinationRect.X, -destinationRect.Y);
|
|
|
- result.Matrix = matrix;
|
|
|
- }
|
|
|
-
|
|
|
- return result;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public static SurfacePattern CreateVisualBrush(VisualBrush brush, Size targetSize)
|
|
|
- {
|
|
|
- var visual = brush.Visual;
|
|
|
-
|
|
|
- if (visual == null)
|
|
|
- {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- var layoutable = visual as ILayoutable;
|
|
|
-
|
|
|
- if (layoutable?.IsArrangeValid == false)
|
|
|
- {
|
|
|
- layoutable.Measure(Size.Infinity);
|
|
|
- layoutable.Arrange(new Rect(layoutable.DesiredSize));
|
|
|
- }
|
|
|
-
|
|
|
- // TODO: This is directly ported from Direct2D and could probably be made more
|
|
|
- // efficient on cairo by taking advantage of the fact that cairo has Extend.None.
|
|
|
- var tileMode = brush.TileMode;
|
|
|
- var sourceRect = brush.SourceRect.ToPixels(layoutable.Bounds.Size);
|
|
|
- var destinationRect = brush.DestinationRect.ToPixels(targetSize);
|
|
|
- var scale = brush.Stretch.CalculateScaling(destinationRect.Size, sourceRect.Size);
|
|
|
- var translate = CalculateTranslate(brush, sourceRect, destinationRect, scale);
|
|
|
- var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size);
|
|
|
|
|
|
- using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height))
|
|
|
+ using (var intermediate = new ImageSurface(Format.ARGB32, (int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height))
|
|
|
using (var ctx = new RenderTarget(intermediate).CreateDrawingContext())
|
|
|
{
|
|
|
- Rect drawRect;
|
|
|
- var transform = CalculateIntermediateTransform(
|
|
|
- tileMode,
|
|
|
- sourceRect,
|
|
|
- destinationRect,
|
|
|
- scale,
|
|
|
- translate,
|
|
|
- out drawRect);
|
|
|
-
|
|
|
- using (ctx.PushClip(drawRect))
|
|
|
- using (ctx.PushPostTransform(transform))
|
|
|
- {
|
|
|
- ctx.Render(visual);
|
|
|
- }
|
|
|
+ helper.DrawIntermediate(ctx);
|
|
|
|
|
|
var result = new SurfacePattern(intermediate);
|
|
|
|
|
|
@@ -135,7 +42,7 @@ namespace Perspex.Cairo.Media
|
|
|
if (brush.TileMode != TileMode.None)
|
|
|
{
|
|
|
var matrix = result.Matrix;
|
|
|
- matrix.InitTranslate(-destinationRect.X, -destinationRect.Y);
|
|
|
+ matrix.InitTranslate(-helper.DestinationRect.X, -helper.DestinationRect.Y);
|
|
|
result.Matrix = matrix;
|
|
|
}
|
|
|
|
|
|
@@ -143,83 +50,6 @@ namespace Perspex.Cairo.Media
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Calculates a translate based on a <see cref="TileBrush"/>, a source and destination
|
|
|
- /// rectangle and a scale.
|
|
|
- /// </summary>
|
|
|
- /// <param name="brush">The brush.</param>
|
|
|
- /// <param name="sourceRect">The source rectangle.</param>
|
|
|
- /// <param name="destinationRect">The destination rectangle.</param>
|
|
|
- /// <param name="scale">The scale factor.</param>
|
|
|
- /// <returns>A vector with the X and Y translate.</returns>
|
|
|
- private static Vector CalculateTranslate(
|
|
|
- TileBrush brush,
|
|
|
- Rect sourceRect,
|
|
|
- Rect destinationRect,
|
|
|
- Vector scale)
|
|
|
- {
|
|
|
- var x = 0.0;
|
|
|
- var y = 0.0;
|
|
|
- var size = sourceRect.Size * scale;
|
|
|
-
|
|
|
- switch (brush.AlignmentX)
|
|
|
- {
|
|
|
- case AlignmentX.Center:
|
|
|
- x += (destinationRect.Width - size.Width) / 2;
|
|
|
- break;
|
|
|
- case AlignmentX.Right:
|
|
|
- x += destinationRect.Width - size.Width;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- switch (brush.AlignmentY)
|
|
|
- {
|
|
|
- case AlignmentY.Center:
|
|
|
- y += (destinationRect.Height - size.Height) / 2;
|
|
|
- break;
|
|
|
- case AlignmentY.Bottom:
|
|
|
- y += destinationRect.Height - size.Height;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return new Vector(x, y);
|
|
|
- }
|
|
|
-
|
|
|
- private static Size CalculateIntermediateSize(
|
|
|
- TileMode tileMode,
|
|
|
- Size targetSize,
|
|
|
- Size destinationSize)
|
|
|
- {
|
|
|
- var result = tileMode == TileMode.None ? targetSize : destinationSize;
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- private static Matrix CalculateIntermediateTransform(
|
|
|
- TileMode tileMode,
|
|
|
- Rect sourceRect,
|
|
|
- Rect destinationRect,
|
|
|
- Vector scale,
|
|
|
- Vector translate,
|
|
|
- out Rect drawRect)
|
|
|
- {
|
|
|
- var transform = Matrix.CreateTranslation(-sourceRect.Position) *
|
|
|
- Matrix.CreateScale(scale) *
|
|
|
- Matrix.CreateTranslation(translate);
|
|
|
- Rect dr;
|
|
|
-
|
|
|
- if (tileMode == TileMode.None)
|
|
|
- {
|
|
|
- dr = destinationRect;
|
|
|
- transform *= Matrix.CreateTranslation(destinationRect.Position);
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- dr = new Rect(destinationRect.Size);
|
|
|
- }
|
|
|
-
|
|
|
- drawRect = dr;
|
|
|
-
|
|
|
- return transform;
|
|
|
- }
|
|
|
+
|
|
|
}
|
|
|
}
|