Skip to content

Commit

Permalink
feat(imageBrush): [WASM] Add support of the ImageBrush
Browse files Browse the repository at this point in the history
  • Loading branch information
dr1rrb committed Jun 10, 2020
1 parent 4a42c78 commit 7af4d1e
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 172 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ private void OnSampleLoaded(object sender, RoutedEventArgs e)
{
for (var i = 0; i < bitmap.PixelBuffer.Length; i += pixel.Length)
{
pixels.Write(pixel);
pixels.Write(pixel, 0, pixel.Length);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Uno.UI.Wasm/WasmScripts/Uno.UI.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ declare namespace Uno.UI {
private measureElement;
private measureViewInternal;
scrollTo(pParams: number): boolean;
setImageRawData(viewId: number, dataPtr: number, width: number, height: number): string;
rawPixelsToBase64EncodeImage(dataPtr: number, width: number, height: number): string;
/**
* Sets the provided image with a mono-chrome version of the provided url.
* @param viewId the image to manipulate
Expand Down
35 changes: 15 additions & 20 deletions src/Uno.UI.Wasm/WasmScripts/Uno.UI.js
Original file line number Diff line number Diff line change
Expand Up @@ -1436,26 +1436,21 @@ var Uno;
elt.scrollTo(opts);
return true;
}
setImageRawData(viewId, dataPtr, width, height) {
const element = this.getView(viewId);
if (element.tagName.toUpperCase() === "IMG") {
const imgElement = element;
const rawCanvas = document.createElement("canvas");
rawCanvas.width = width;
rawCanvas.height = height;
const ctx = rawCanvas.getContext("2d");
const imgData = ctx.createImageData(width, height);
const bufferSize = width * height * 4;
for (let i = 0; i < bufferSize; i += 4) {
imgData.data[i + 0] = Module.HEAPU8[dataPtr + i + 2];
imgData.data[i + 1] = Module.HEAPU8[dataPtr + i + 1];
imgData.data[i + 2] = Module.HEAPU8[dataPtr + i + 0];
imgData.data[i + 3] = Module.HEAPU8[dataPtr + i + 3];
}
ctx.putImageData(imgData, 0, 0);
imgElement.src = rawCanvas.toDataURL();
return "ok";
}
rawPixelsToBase64EncodeImage(dataPtr, width, height) {
const rawCanvas = document.createElement("canvas");
rawCanvas.width = width;
rawCanvas.height = height;
const ctx = rawCanvas.getContext("2d");
const imgData = ctx.createImageData(width, height);
const bufferSize = width * height * 4;
for (let i = 0; i < bufferSize; i += 4) {
imgData.data[i + 0] = Module.HEAPU8[dataPtr + i + 2];
imgData.data[i + 1] = Module.HEAPU8[dataPtr + i + 1];
imgData.data[i + 2] = Module.HEAPU8[dataPtr + i + 0];
imgData.data[i + 3] = Module.HEAPU8[dataPtr + i + 3];
}
ctx.putImageData(imgData, 0, 0);
return rawCanvas.toDataURL();
}
/**
* Sets the provided image with a mono-chrome version of the provided url.
Expand Down
38 changes: 15 additions & 23 deletions src/Uno.UI.Wasm/ts/WindowManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1464,33 +1464,25 @@ namespace Uno.UI {
return true;
}

public setImageRawData(viewId: number, dataPtr: number, width: number, height: number): string {
const element = this.getView(viewId);

if (element.tagName.toUpperCase() === "IMG") {
const imgElement = element as HTMLImageElement;

const rawCanvas = document.createElement("canvas");
rawCanvas.width = width;
rawCanvas.height = height;
public rawPixelsToBase64EncodeImage(dataPtr: number, width: number, height: number): string {
const rawCanvas = document.createElement("canvas");
rawCanvas.width = width;
rawCanvas.height = height;

const ctx = rawCanvas.getContext("2d");
const imgData = ctx.createImageData(width, height);
const ctx = rawCanvas.getContext("2d");
const imgData = ctx.createImageData(width, height);

const bufferSize = width * height * 4;
const bufferSize = width * height * 4;

for (let i = 0; i < bufferSize; i += 4) {
imgData.data[i + 0] = Module.HEAPU8[dataPtr + i + 2];
imgData.data[i + 1] = Module.HEAPU8[dataPtr + i + 1];
imgData.data[i + 2] = Module.HEAPU8[dataPtr + i + 0];
imgData.data[i + 3] = Module.HEAPU8[dataPtr + i + 3];
}
ctx.putImageData(imgData, 0, 0);

imgElement.src = rawCanvas.toDataURL();

return "ok";
for (let i = 0; i < bufferSize; i += 4) {
imgData.data[i + 0] = Module.HEAPU8[dataPtr + i + 2];
imgData.data[i + 1] = Module.HEAPU8[dataPtr + i + 1];
imgData.data[i + 2] = Module.HEAPU8[dataPtr + i + 0];
imgData.data[i + 3] = Module.HEAPU8[dataPtr + i + 3];
}
ctx.putImageData(imgData, 0, 0);

return rawCanvas.toDataURL();
}


Expand Down
130 changes: 24 additions & 106 deletions src/Uno.UI/UI/Xaml/Controls/Image/Image.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public HtmlImage() : base("img")
partial class Image : FrameworkElement, ICustomClippingElement
{
private readonly SerialDisposable _sourceDisposable = new SerialDisposable();
private static readonly string UNO_BOOTSTRAP_APP_BASE = global::System.Environment.GetEnvironmentVariable(nameof(UNO_BOOTSTRAP_APP_BASE));

private readonly HtmlImage _htmlImage;
private Size _lastMeasuredSize;
Expand Down Expand Up @@ -91,134 +90,53 @@ public ImageSource Source
set => SetValue(SourceProperty, value);
}

// Using a DependencyProperty as the backing store for Source. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SourceProperty =
DependencyProperty.Register("Source", typeof(ImageSource), typeof(Image), new PropertyMetadata(null, (s, e) => ((Image)s)?.OnSourceChanged(e)));

private void OnSourceChanged(DependencyPropertyChangedEventArgs e)
{
UpdateHitTest();

var source = e.NewValue as ImageSource;

_lastMeasuredSize = _zeroSize;

var stream = source?.Stream;
if (stream != null)
{
stream.Position = 0;
var encodedBytes = Convert.ToBase64String(stream.ReadBytes());
var url = "data:application/octet-stream;base64," + encodedBytes;
SetImageUrl(url);
}
else if (source is WriteableBitmap wb)
{
void setImageContent()
{
if (wb.PixelBuffer is InMemoryBuffer mb)
{
var gch = GCHandle.Alloc(mb.Data, GCHandleType.Pinned);
var pinnedData = gch.AddrOfPinnedObject();

try
{
WebAssemblyRuntime.InvokeJS(
"Uno.UI.WindowManager.current.setImageRawData(" + _htmlImage.HtmlId + ", " + pinnedData + ", " + wb.PixelWidth + ", " + wb.PixelHeight + ");"
);

InvalidateMeasure();
}
finally
{
gch.Free();
}
}
}

void OnInvalidated(object sdn, EventArgs args)
{
setImageContent();
}

wb.Invalidated += OnInvalidated;
_sourceDisposable.Disposable = Disposable.Create(() => wb.Invalidated -= OnInvalidated);
setImageContent();
}
else
if (e.NewValue is ImageSource source)
{
void setImageContent()
_sourceDisposable.Disposable = source.Subscribe(img =>
{
var url = source?.WebUri;

if (url != null)
switch (img.Kind)
{
if (url.IsAbsoluteUri)
{
if (url.Scheme.Equals("file", StringComparison.OrdinalIgnoreCase))
case ImageDataKind.Empty:
_htmlImage.SetAttribute("src", "");
break;
case ImageDataKind.Base64:
case ImageDataKind.Url:
default:
if (MonochromeColor != null)
{
// Local files are assumed as coming from the remoter server
SetImageUrl(url.PathAndQuery);
WebAssemblyRuntime.InvokeJS("Uno.UI.WindowManager.current.setImageAsMonochrome("
+ _htmlImage.HtmlId + ", \""
+ img.Value + "\", \""
+ MonochromeColor.Value.ToHexString() + "\");");
}
else
{
SetImageUrl(url.AbsoluteUri);
_htmlImage.SetAttribute("src", img.Value);
}
}
else
{
SetImageUrl(url.OriginalString);
}
}
else
{
SetImageUrl("");
}
}
break;
_sourceDisposable.Disposable = null;

_sourceDisposable.Disposable =
Source?.RegisterDisposablePropertyChangedCallback(
BitmapImage.UriSourceProperty, (o, args) =>
{
if (!object.Equals(e.OldValue, args.NewValue))
{
setImageContent();
}
}
);

setImageContent();
}
}

private void SetImageUrl(string url)
{
if (MonochromeColor != null)
{
WebAssemblyRuntime.InvokeJS(
"Uno.UI.WindowManager.current.setImageAsMonochrome(" + _htmlImage.HtmlId + ", \"" + url + "\", \"" + MonochromeColor.Value.ToHexString() + "\");"
);
case ImageDataKind.Error:
_htmlImage.SetAttribute("src", "");
_htmlImage.InternalDispatchEvent("error", EventArgs.Empty);
break;
}
});
}
else
{
if (UNO_BOOTSTRAP_APP_BASE != null)
{
var adjustedUrl = url switch
{
string u when u.StartsWith("http:/", StringComparison.OrdinalIgnoreCase) || u.StartsWith("https:/", StringComparison.OrdinalIgnoreCase) => url,
_ => UNO_BOOTSTRAP_APP_BASE + "/" + url
};

_htmlImage.SetAttribute("src", adjustedUrl);
}
else
{
_htmlImage.SetAttribute("src", url);
}
_htmlImage.SetAttribute("src", "");
}
}

#endregion

public static readonly DependencyProperty StretchProperty =
Expand Down
29 changes: 28 additions & 1 deletion src/Uno.UI/UI/Xaml/FrameworkElement.Interface.wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,32 @@ protected virtual void OnBackgroundChanged(DependencyPropertyChangedEventArgs e)
var brush = e.NewValue as Brush;
SetBackgroundBrush(brush);

_backgroundSubscription.Disposable = Brush.AssignAndObserveBrush(brush, _ => SetBackgroundBrush(brush));
if (brush is ImageBrush imgBrush)
{
RecalculateBrushOnSizeChanged(false);
_backgroundSubscription.Disposable = imgBrush.Subscribe(img =>
{
switch (img.Kind)
{
case ImageDataKind.Empty:
case ImageDataKind.Error:
ResetStyle("background-color");
ResetStyle("background-image");
break;
case ImageDataKind.Base64:
case ImageDataKind.Url:
default:
ResetStyle("background-color");
SetStyle("background-image", "url(" + img.Value + ")");
break;
}
});
}
else
{
_backgroundSubscription.Disposable = Brush.AssignAndObserveBrush(brush, _ => SetBackgroundBrush(brush));
}
}

private protected void SetBackgroundBrush(Brush brush)
Expand All @@ -130,9 +155,11 @@ private protected void SetBackgroundBrush(Brush brush)
case SolidColorBrush solidColorBrush:
var color = solidColorBrush.ColorWithOpacity;
SetStyle("background-color", color.ToHexString());
ResetStyle("background-image");
RecalculateBrushOnSizeChanged(false);
break;
case GradientBrush gradientBrush:
ResetStyle("background-color");
SetStyle("background-image", gradientBrush.ToCssString(RenderSize));
RecalculateBrushOnSizeChanged(true);
break;
Expand Down
12 changes: 8 additions & 4 deletions src/Uno.UI/UI/Xaml/Media/Brush.wasm.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;
using System.Text;
using Uno.Extensions;
using Uno.Disposables;
Expand All @@ -9,7 +10,6 @@ public abstract partial class Brush
{
internal static IDisposable AssignAndObserveBrush(Brush b, Action<Windows.UI.Color> colorSetter)
{

if (b is SolidColorBrush colorBrush)
{
var disposables = new CompositeDisposable(2);
Expand Down Expand Up @@ -50,12 +50,16 @@ internal static IDisposable AssignAndObserveBrush(Brush b, Action<Windows.UI.Col

return disposables;
}
// ImageBrush not supported yet on Wasm
else

if (b is ImageBrush)
{
colorSetter(SolidColorBrushHelper.Transparent.Color);
Application.Current.RaiseRecoverableUnhandledException(new InvalidOperationException(
"ImageBrush is ** not ** supported by the AssignAndObserveBrush. "
+ "(Instead you have to use the ImageBrush.Subscribe().)"));
return Disposable.Empty;
}

colorSetter(SolidColorBrushHelper.Transparent.Color);
return Disposable.Empty;
}
}
Expand Down
Loading

0 comments on commit 7af4d1e

Please sign in to comment.