Skip to content

Commit

Permalink
feat: Support CompositionStretch and [Horizontal|Vertical]AlignmentRa…
Browse files Browse the repository at this point in the history
…tio and use that for ImageBrush
  • Loading branch information
Youssef1313 committed Oct 26, 2023
1 parent f3f0051 commit b0854ef
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 93 deletions.
80 changes: 80 additions & 0 deletions src/Uno.Foundation/SizeExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
namespace Windows.Foundation;

internal static class SizeExtensions
{
internal static double AspectRatio(this Size size)
{
var w = size.Width;
var h = size.Height;
switch (w)
{
case double.NegativeInfinity:
return -1;
case double.PositiveInfinity:
return 1;
case double.NaN:
return 1;
case 0.0d:
return 1;
}
switch (h)
{
case double.NegativeInfinity:
return -1;
case double.PositiveInfinity:
return 1;
case double.NaN:
return 1;
case 0.0d:
return 1; // special case
case 1.0d:
return w;
}
return w / h;
}

#if __IOS__ || __MACOS__
internal static double AspectRatio(this CoreGraphics.CGSize size)
{
var w = size.Width;
var h = size.Height;
if (w == nfloat.NegativeInfinity)
{
return -1;
}
else if (w == nfloat.PositiveInfinity)
{
return 1;
}
else if (w == nfloat.NaN)
{
return 1;
}
else if (w == 0.0d)
{
return 1;
}
if (h == nfloat.NegativeInfinity)
{
return -1;
}
else if (h == nfloat.PositiveInfinity)
{
return 1;
}
else if (h == nfloat.NaN)
{
return 1;
}
else if (h == 0.0d)
{
return 1; // special case
}
else if (h == 1.0d)
{
return w;
}
return w / h;
}
#endif
}
67 changes: 58 additions & 9 deletions src/Uno.UI.Composition/Composition/CompositionSurfaceBrush.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
using System.Numerics;
using Uno.UI.Composition;
using SkiaSharp;
using Windows.Foundation;
using System.Diagnostics.CodeAnalysis;

namespace Windows.UI.Composition
{
public partial class CompositionSurfaceBrush : CompositionBrush, IOnlineBrush, ISizedBrush
{
bool IOnlineBrush.IsOnline => Surface is ISkiaSurface skiaSurface;
bool IOnlineBrush.IsOnline => Surface is ISkiaSurface;

bool ISizedBrush.IsSized => true;

Expand All @@ -24,19 +26,66 @@ public partial class CompositionSurfaceBrush : CompositionBrush, IOnlineBrush, I
};
}

internal override void UpdatePaint(SKPaint fillPaint, SKRect bounds)
private Rect GetArrangedImageRect(Size sourceSize, SKRect targetRect)
{
if (Surface is SkiaCompositionSurface scs)
{
var imageShader = SKShader.CreateImage(scs.Image, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, TransformMatrix.ToSKMatrix());
var size = GetArrangedImageSize(sourceSize, targetRect.Size.ToSize());

fillPaint.Shader = imageShader;
var point = new Point(targetRect.Left, targetRect.Top);
point.X += (targetRect.Width - size.Width) * HorizontalAlignmentRatio;
point.Y += (targetRect.Height - size.Height) * VerticalAlignmentRatio;
return new Rect(point, size);
}

fillPaint.IsAntialias = true;
private Size GetArrangedImageSize(Size sourceSize, Size targetSize)
{
var sourceAspectRatio = sourceSize.AspectRatio();
var targetAspectRatio = targetSize.AspectRatio();
switch (Stretch)
{
default:
case CompositionStretch.None:
return sourceSize;
case CompositionStretch.Fill:
return targetSize;
case CompositionStretch.Uniform:
return targetAspectRatio > sourceAspectRatio
? new Size(sourceSize.Width * targetSize.Height / sourceSize.Height, targetSize.Height)
: new Size(targetSize.Width, sourceSize.Height * targetSize.Width / sourceSize.Width);
case CompositionStretch.UniformToFill:
return targetAspectRatio < sourceAspectRatio
? new Size(sourceSize.Width * targetSize.Height / sourceSize.Height, targetSize.Height)
: new Size(targetSize.Width, sourceSize.Height * targetSize.Width / sourceSize.Width);
}
}

private static bool TryGetSkiaCompositionSurface(ICompositionSurface? surface, [NotNullWhen(true)] out SkiaCompositionSurface? skiaCompositionSurface)
{
if (surface is SkiaCompositionSurface scs)
{
skiaCompositionSurface = scs;
return true;
}
else if (Surface is ISkiaCompositionSurfaceProvider scsp && scsp.SkiaCompositionSurface is SkiaCompositionSurface scsps)
else if (surface is ISkiaCompositionSurfaceProvider scsp && scsp.SkiaCompositionSurface is SkiaCompositionSurface scsps)
{
skiaCompositionSurface = scsps;
return true;
}

skiaCompositionSurface = null;
return false;
}

internal override void UpdatePaint(SKPaint fillPaint, SKRect bounds)
{
if (TryGetSkiaCompositionSurface(Surface, out var scs))
{
var imageShader = SKShader.CreateImage(scsps.Image, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, TransformMatrix.ToSKMatrix());
var sourceImageSize = new Size(scs.Image!.Width, scs.Image.Height);
var backgroundArea = GetArrangedImageRect(sourceImageSize, bounds);
var matrix = Matrix3x2.CreateScale((float)(backgroundArea.Width / sourceImageSize.Width), (float)(backgroundArea.Height / sourceImageSize.Height));
matrix *= Matrix3x2.CreateTranslation((float)backgroundArea.Left, (float)backgroundArea.Top);
matrix *= TransformMatrix;

var imageShader = SKShader.CreateImage(scs.Image, SKShaderTileMode.Decal, SKShaderTileMode.Decal, matrix.ToSKMatrix());

fillPaint.Shader = imageShader;

Expand Down
84 changes: 0 additions & 84 deletions src/Uno.UI/UI/LayoutHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,90 +403,6 @@ internal static bool IsEnclosedBy(this Rect enclosee, Rect encloser)
[Pure]
internal static double AspectRatio(this Rect rect) => rect.Size.AspectRatio();

[Pure]
internal static double AspectRatio(this Size size)
{
var w = size.Width;
var h = size.Height;

switch (w)
{
case NegativeInfinity:
return -1;
case PositiveInfinity:
return 1;
case NaN:
return 1;
case 0.0d:
return 1;
}

switch (h)
{
case NegativeInfinity:
return -1;
case PositiveInfinity:
return 1;
case NaN:
return 1;
case 0.0d:
return 1; // special case
case 1.0d:
return w;
}

return w / h;
}

#if __IOS__ || __MACOS__
[Pure]
internal static double AspectRatio(this CoreGraphics.CGSize size)
{
var w = size.Width;
var h = size.Height;

if (w == nfloat.NegativeInfinity)
{
return -1;
}
else if (w == nfloat.PositiveInfinity)
{
return 1;
}
else if (w == nfloat.NaN)
{
return 1;
}
else if (w == 0.0d)
{
return 1;
}

if (h == nfloat.NegativeInfinity)
{
return -1;
}
else if (h == nfloat.PositiveInfinity)
{
return 1;
}
else if (h == nfloat.NaN)
{
return 1;
}
else if (h == 0.0d)
{
return 1; // special case
}
else if (h == 1.0d)
{
return w;
}

return w / h;
}
#endif

[Pure]
internal static Rect GetBoundsRectRelativeTo(this FrameworkElement element, FrameworkElement relativeTo)
{
Expand Down
25 changes: 25 additions & 0 deletions src/Uno.UI/UI/Xaml/Media/Brush.skia.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ private static IDisposable AsssignAndObserveImageBrush(ImageBrush brush, Composi
{
if (brush.ImageDataCache is { } data)
{
surfaceBrush.Stretch = (CompositionStretch)brush.Stretch;
surfaceBrush.HorizontalAlignmentRatio = GetHorizontalAlignmentRatio(brush.AlignmentX);
surfaceBrush.VerticalAlignmentRatio = GetVerticalAlignmentRatio(brush.AlignmentY);
surfaceBrush.Surface = data.CompositionSurface;
}
};
Expand All @@ -158,6 +161,28 @@ private static IDisposable AsssignAndObserveImageBrush(ImageBrush brush, Composi
return new DisposableAction(() => brush.InvalidateRender -= onInvalidateRender);
}

private static float GetHorizontalAlignmentRatio(AlignmentX alignmentX)
{
return alignmentX switch
{
AlignmentX.Left => 0.0f,
AlignmentX.Center => 0.5f,
AlignmentX.Right => 1.0f,
_ => 0.5f, // this should never happen.
};
}

private static float GetVerticalAlignmentRatio(AlignmentY alignmentY)
{
return alignmentY switch
{
AlignmentY.Top => 0.0f,
AlignmentY.Center => 0.5f,
AlignmentY.Bottom => 1.0f,
_ => 0.5f, // this should never happen.
};
}

private static IDisposable AssignAndObserveRadialGradientBrush(RadialGradientBrush brush, Compositor compositor, BrushSetterHandler brushSetter)
{
var disposables = new CompositeDisposable();
Expand Down

0 comments on commit b0854ef

Please sign in to comment.