Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(WriteableBitmap): Invalidation should cause a redrawing #18408

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices.WindowsRuntime;
using Uno.UI.Samples.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
Expand All @@ -24,26 +26,18 @@ public WriteableBitmap_MultiInvalidate()
private void UpdateSource(object sender, RoutedEventArgs e)
{
var randomizer = new Random(_seed++);
if (_bitmap.PixelBuffer is Windows.Storage.Streams.Buffer buffer
&& buffer.GetType().GetField("_data", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(buffer) is Memory<byte> data)
var stream = _bitmap.PixelBuffer.AsStream();
var length = _bitmap.PixelBuffer.Length;
for (var i = 0; i < length; i++)
{
var span = data.Span;
for (var i = 0; i < data.Length; i++)
if (i % 4 == 3)
{
if (i % 4 == 3)
{
// Alpha channel
span[i] = 255;
}
else
{
span[i] = (byte)randomizer.Next(256);
}
stream.WriteByte(255);
}
else
{
stream.WriteByte((byte)randomizer.Next(256));
}
}
else
{
throw new InvalidOperationException("Could not access _data field in Buffer type.");
}

// This request to the image to redraw the buffer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,119 +133,6 @@ public void When_SetSourceAsync_Stream_Then_StreamClonedSynchronously()
}
#endif

[TestMethod]
[RunsOnUIThread]
public async Task When_WriteableBitmap_Assigned_With_Data_Present()
{
if (!ApiInformation.IsTypePresent("Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap"))
{
Assert.Inconclusive(); // System.NotImplementedException: RenderTargetBitmap is not supported on this platform.;
}

var wb = new WriteableBitmap(20, 20);

var parent = new Border()
{
Width = 50,
Height = 50,
Background = new SolidColorBrush(Colors.Blue),
};

var rect = new Rectangle
{
Width = 20,
Height = 20,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
};

parent.Child = rect;

WindowHelper.WindowContent = parent;

await WindowHelper.WaitForIdle();
await WindowHelper.WaitForLoaded(rect);

var bgraPixelData = Enumerable.Repeat<byte>(255, (int)wb.PixelBuffer.Length).ToArray();

using (Stream stream = wb.PixelBuffer.AsStream())
{
stream.Write(bgraPixelData, 0, bgraPixelData.Length);
}

rect.Fill = new ImageBrush
{
ImageSource = wb
};

await WindowHelper.WaitForIdle();

var snapshot = await UITestHelper.ScreenShot(parent);
var coords = parent.GetRelativeCoords(rect);
await WindowHelper.WaitForIdle();

ImageAssert.DoesNotHaveColorInRectangle(
snapshot, new System.Drawing.Rectangle((int)coords.X, (int)coords.Y, (int)coords.Width, (int)coords.Height), Colors.Blue);
}

[TestMethod]
[RunsOnUIThread]
#if __WASM__
[Ignore("https://github.com/unoplatform/uno/issues/12445")]
#endif
public async Task When_WriteableBitmap_SetSource_Should_Update_PixelWidth_And_PixelHeight()
{
if (!ApiInformation.IsTypePresent("Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap"))
{
Assert.Inconclusive(); // System.NotImplementedException: RenderTargetBitmap is not supported on this platform.;
}

var writeableBitmap = new WriteableBitmap(1, 1);
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/ingredient3.png"));
using (var stream = await file.OpenReadAsync())
{
await writeableBitmap.SetSourceAsync(stream);
}

Assert.AreEqual(147, writeableBitmap.PixelWidth);
Assert.AreEqual(147, writeableBitmap.PixelHeight);

var parent = new Border()
{
Width = 147,
Height = 147,
};

var rect = new Rectangle
{
Width = 147,
Height = 147,
};

parent.Child = rect;

WindowHelper.WindowContent = parent;

await WindowHelper.WaitForIdle();
await WindowHelper.WaitForLoaded(rect);

rect.Fill = new ImageBrush
{
ImageSource = writeableBitmap
};

await WindowHelper.WaitForIdle();

var renderer = new RenderTargetBitmap();

await renderer.RenderAsync(parent);
var snapshot = await RawBitmap.From(renderer, rect);

#if !__IOS__ && !__MACOS__ // https://github.com/unoplatform/uno/issues/12705
ImageAssert.HasColorAt(snapshot, 1, 1, Color.FromArgb(0xFF, 0xFA, 0xB8, 0x63), tolerance: 5);
#endif
}

[TestMethod]
[RunsOnUIThread]
public async Task When_ImageBrush_Source_Changes()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Markup;
using Microsoft.UI.Xaml.Media.Imaging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.InteropServices.WindowsRuntime;
using Private.Infrastructure;
using Windows.Storage;
using static Private.Infrastructure.TestServices;
using Microsoft.UI.Xaml.Shapes;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
using Uno.UI.RuntimeTests.Helpers;
using Uno.UI.RuntimeTests.Extensions;
using Windows.Foundation.Metadata;
using Microsoft.UI.Xaml;
using System.Linq;

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Media_Imaging
{
[TestClass]
[RunsOnUIThread]
public class Given_WriteableBitmap
{
[TestMethod]
#if __IOS__
[Ignore("fails")]
#endif
public async Task When_Invalidated()
{
if (!ApiInformation.IsTypePresent("Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap"))
{
Assert.Inconclusive(); // System.NotImplementedException: RenderTargetBitmap is not supported on this platform.;

Check warning on line 38 in src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Media_Imaging/Given_WriteableBitmap.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Media_Imaging/Given_WriteableBitmap.cs#L38

Remove this commented out code.
}

var seed = 42;
var bitmap = new WriteableBitmap(200, 200);
var border = new Border
{
Width = 200,
Height = 200,
Background = new ImageBrush { ImageSource = bitmap }
};

UpdateSource();

await UITestHelper.Load(border);

var snapshot1 = await UITestHelper.ScreenShot(border);

UpdateSource();
await WindowHelper.WaitForIdle();

var snapshot2 = await UITestHelper.ScreenShot(border);
await ImageAssert.AreNotEqualAsync(snapshot1, snapshot2);

void UpdateSource()
{
var randomizer = new Random(seed++);
var stream = bitmap.PixelBuffer.AsStream();
var length = bitmap.PixelBuffer.Length;
for (var i = 0; i < length; i++)
{
if (i % 4 == 3)

Check failure on line 69 in src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Media_Imaging/Given_WriteableBitmap.cs

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Media_Imaging/Given_WriteableBitmap.cs#L69

The result of this modulus operation may not be positive.
{
stream.WriteByte(255);
}
else
{
stream.WriteByte((byte)randomizer.Next(256));
}
}

bitmap.Invalidate();
}
}

[TestMethod]
[RunsOnUIThread]
public async Task When_WriteableBitmap_Assigned_With_Data_Present()
{
if (!ApiInformation.IsTypePresent("Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap"))
{
Assert.Inconclusive(); // System.NotImplementedException: RenderTargetBitmap is not supported on this platform.;
}

var wb = new WriteableBitmap(20, 20);

var parent = new Border()
{
Width = 50,
Height = 50,
Background = new SolidColorBrush(Colors.Blue),
};

var rect = new Rectangle
{
Width = 20,
Height = 20,
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
};

parent.Child = rect;

WindowHelper.WindowContent = parent;

await WindowHelper.WaitForIdle();
await WindowHelper.WaitForLoaded(rect);

var bgraPixelData = Enumerable.Repeat<byte>(255, (int)wb.PixelBuffer.Length).ToArray();

using (Stream stream = wb.PixelBuffer.AsStream())
{
stream.Write(bgraPixelData, 0, bgraPixelData.Length);
}

rect.Fill = new ImageBrush
{
ImageSource = wb
};

await WindowHelper.WaitForIdle();

var snapshot = await UITestHelper.ScreenShot(parent);
var coords = parent.GetRelativeCoords(rect);
await WindowHelper.WaitForIdle();

ImageAssert.DoesNotHaveColorInRectangle(
snapshot, new System.Drawing.Rectangle((int)coords.X, (int)coords.Y, (int)coords.Width, (int)coords.Height), Colors.Blue);
}

[TestMethod]
[RunsOnUIThread]
#if __WASM__
[Ignore("https://github.com/unoplatform/uno/issues/12445")]
#endif
public async Task When_WriteableBitmap_SetSource_Should_Update_PixelWidth_And_PixelHeight()
{
if (!ApiInformation.IsTypePresent("Microsoft.UI.Xaml.Media.Imaging.RenderTargetBitmap"))
{
Assert.Inconclusive(); // System.NotImplementedException: RenderTargetBitmap is not supported on this platform.;
}

var writeableBitmap = new WriteableBitmap(1, 1);
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/ingredient3.png"));
using (var stream = await file.OpenReadAsync())
{
await writeableBitmap.SetSourceAsync(stream);
}

Assert.AreEqual(147, writeableBitmap.PixelWidth);
Assert.AreEqual(147, writeableBitmap.PixelHeight);

var parent = new Border()
{
Width = 147,
Height = 147,
};

var rect = new Rectangle
{
Width = 147,
Height = 147,
};

parent.Child = rect;

WindowHelper.WindowContent = parent;

await WindowHelper.WaitForIdle();
await WindowHelper.WaitForLoaded(rect);

rect.Fill = new ImageBrush
{
ImageSource = writeableBitmap
};

await WindowHelper.WaitForIdle();

var renderer = new RenderTargetBitmap();

await renderer.RenderAsync(parent);
var snapshot = await RawBitmap.From(renderer, rect);

#if !__IOS__ && !__MACOS__ // https://github.com/unoplatform/uno/issues/12705
ImageAssert.HasColorAt(snapshot, 1, 1, Color.FromArgb(0xFF, 0xFA, 0xB8, 0x63), tolerance: 5);
#endif
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ partial class BorderLayerRenderer
private static ImageSource? GetBackgroundImageSource(BorderLayerState? state)
=> (state?.Background as ImageBrush)?.ImageSource;

partial void UpdatePlatform()
partial void UpdatePlatform(bool forceUpdate)
{
var drawArea = new Rect(default, _owner.LayoutSlotWithMarginsAndAlignments.Size.LogicalToPhysicalPixels());
var newState = new BorderLayerState(drawArea.Size, _borderInfoProvider);
var previousLayoutState = _currentState;

if (newState.Equals(previousLayoutState))
if (newState.Equals(previousLayoutState) && !forceUpdate)
{
return;
}
Expand Down Expand Up @@ -261,8 +261,8 @@ private IDisposable InnerCreateLayers(
// because even though the brush instance is the same, there are additional properties
// that BorderLayerState tracks on Android. This is not ideal and we should avoid it by refactoring
// this file to handle brush changes on the same brush instance on its own instead.
Brush.SetupBrushChanged(_currentState.Background, background, ref _backgroundChanged, () => Update(), false);
Brush.SetupBrushChanged(_currentState.BorderBrush, borderBrush, ref _borderChanged, () => Update(), false);
Brush.SetupBrushChanged(_currentState.Background, background, ref _backgroundChanged, () => Update(true), false);
Brush.SetupBrushChanged(_currentState.BorderBrush, borderBrush, ref _borderChanged, () => Update(true), false);

return disposables;
}
Expand Down
Loading
Loading