245 lines
8.4 KiB
C#
245 lines
8.4 KiB
C#
using System;
|
|
using System.ComponentModel;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using Xamarin.Forms;
|
|
|
|
namespace Ooui.Forms.Renderers
|
|
{
|
|
public class ImageRenderer : ViewRenderer<Xamarin.Forms.Image, Ooui.Image>
|
|
{
|
|
bool _isDisposed;
|
|
double ClientHeight = -1;
|
|
double ClientWidth = -1;
|
|
|
|
protected override void Dispose (bool disposing)
|
|
{
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
if (disposing) {
|
|
}
|
|
|
|
_isDisposed = true;
|
|
|
|
base.Dispose (disposing);
|
|
}
|
|
|
|
protected override async void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.Image> e)
|
|
{
|
|
if (Control == null) {
|
|
var imageView = new Ooui.Image ();
|
|
SetNativeControl (imageView);
|
|
this.Style.Overflow = "hidden";
|
|
|
|
Control.Loaded += OnLoad;
|
|
}
|
|
|
|
if (e.NewElement != null) {
|
|
SetAspect ();
|
|
await TrySetImage (e.OldElement);
|
|
SetOpacity ();
|
|
}
|
|
|
|
base.OnElementChanged (e);
|
|
}
|
|
|
|
protected override async void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e)
|
|
{
|
|
base.OnElementPropertyChanged (sender, e);
|
|
if (e.PropertyName == Xamarin.Forms.Image.SourceProperty.PropertyName)
|
|
await TrySetImage ();
|
|
else if (e.PropertyName == Xamarin.Forms.Image.IsOpaqueProperty.PropertyName)
|
|
SetOpacity ();
|
|
else if (e.PropertyName == Xamarin.Forms.Image.AspectProperty.PropertyName)
|
|
SetAspect();
|
|
else if (e.PropertyName == VisualElement.WidthProperty.PropertyName)
|
|
SetDimensions ();
|
|
}
|
|
|
|
void OnLoad(object sender, EventArgs eventArgs)
|
|
{
|
|
var args = (TargetEventArgs)eventArgs;
|
|
ClientHeight = args.ClientHeight;
|
|
ClientWidth = args.ClientWidth;
|
|
|
|
SetDimensions();
|
|
}
|
|
|
|
void SetDimensions()
|
|
{
|
|
var b = Element.Bounds;
|
|
double scale = 1;
|
|
|
|
if (ClientWidth < 0 || ClientHeight < 0)
|
|
return;
|
|
|
|
if (Math.Abs(b.Width) > 0)
|
|
{
|
|
scale = b.Width / ClientWidth;
|
|
Element.WidthRequest = b.Width;
|
|
Element.HeightRequest = scale * ClientHeight;
|
|
}
|
|
else if (Math.Abs(b.Height) > 0)
|
|
{
|
|
scale = b.Height / ClientHeight;
|
|
Element.WidthRequest = scale * ClientWidth;
|
|
Element.HeightRequest = b.Height;
|
|
}
|
|
else
|
|
{
|
|
// We can't really know what to do in this case
|
|
}
|
|
}
|
|
|
|
void SetAspect ()
|
|
{
|
|
if (_isDisposed || Element == null || Control == null) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
protected virtual async Task TrySetImage (Xamarin.Forms.Image previous = null)
|
|
{
|
|
// By default we'll just catch and log any exceptions thrown by SetImage so they don't bring down
|
|
// the application; a custom renderer can override this method and handle exceptions from
|
|
// SetImage differently if it wants to
|
|
|
|
try {
|
|
await SetImage (previous).ConfigureAwait (false);
|
|
}
|
|
catch (Exception ex) {
|
|
System.Diagnostics.Debug.WriteLine ("Error loading image: {0}", ex);
|
|
}
|
|
finally {
|
|
((IImageController)Element)?.SetIsLoading (false);
|
|
}
|
|
}
|
|
|
|
protected async Task SetImage (Xamarin.Forms.Image oldElement = null)
|
|
{
|
|
if (_isDisposed || Element == null || Control == null) {
|
|
return;
|
|
}
|
|
|
|
var source = Element.Source;
|
|
|
|
if (oldElement != null) {
|
|
var oldSource = oldElement.Source;
|
|
if (Equals (oldSource, source))
|
|
return;
|
|
|
|
if (oldSource is FileImageSource && source is FileImageSource && ((FileImageSource)oldSource).File == ((FileImageSource)source).File)
|
|
return;
|
|
|
|
Control.Source = "";
|
|
}
|
|
|
|
IImageSourceHandler handler;
|
|
|
|
Element.SetIsLoading (true);
|
|
|
|
if (source != null &&
|
|
(handler = Xamarin.Forms.Internals.Registrar.Registered.GetHandler<IImageSourceHandler> (source.GetType ())) != null) {
|
|
string uiimage;
|
|
try {
|
|
uiimage = await handler.LoadImageAsync (source, scale: 1.0f);
|
|
}
|
|
catch (OperationCanceledException) {
|
|
uiimage = null;
|
|
}
|
|
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
var imageView = Control;
|
|
if (imageView != null && uiimage != null)
|
|
imageView.Source = uiimage;
|
|
|
|
((IVisualElementController)Element).NativeSizeChanged ();
|
|
}
|
|
else {
|
|
Control.Source = "";
|
|
}
|
|
|
|
Element.SetIsLoading (false);
|
|
}
|
|
|
|
void SetOpacity ()
|
|
{
|
|
if (_isDisposed || Element == null || Control == null) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
public interface IImageSourceHandler : IRegisterable
|
|
{
|
|
Task<string> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken), float scale = 1);
|
|
}
|
|
|
|
public sealed class FileImageSourceHandler : IImageSourceHandler
|
|
{
|
|
#pragma warning disable 1998
|
|
public async Task<string> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken), float scale = 1f)
|
|
{
|
|
string image = null;
|
|
var filesource = imagesource as FileImageSource;
|
|
var file = filesource?.File;
|
|
if (!string.IsNullOrEmpty (file)) {
|
|
var name = System.IO.Path.GetFileName (file);
|
|
image = "/images/" + name;
|
|
if (Xamarin.Forms.PageExtensions.Ui.TryGetFileContentAtPath (image, out var f)) {
|
|
// Already published
|
|
}
|
|
else {
|
|
await Task.Run (() => Xamarin.Forms.PageExtensions.Ui.PublishFile (image, file), cancelationToken);
|
|
}
|
|
}
|
|
return image;
|
|
}
|
|
}
|
|
|
|
public sealed class StreamImagesourceHandler : IImageSourceHandler
|
|
{
|
|
public async Task<string> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken), float scale = 1f)
|
|
{
|
|
string image = null;
|
|
var streamsource = imagesource as StreamImageSource;
|
|
if (streamsource?.Stream != null) {
|
|
using (var streamImage = await ((IStreamImageSource)streamsource).GetStreamAsync (cancelationToken).ConfigureAwait (false)) {
|
|
if (streamImage != null) {
|
|
var data = new byte[streamImage.Length];
|
|
using (var outputStream = new System.IO.MemoryStream (data)) {
|
|
await streamImage.CopyToAsync (outputStream, 4096, cancelationToken).ConfigureAwait (false);
|
|
}
|
|
var hash = Ooui.Utilities.GetShaHash (data);
|
|
var etag = "\"" + hash + "\"";
|
|
image = "/images/" + hash;
|
|
if (Xamarin.Forms.PageExtensions.Ui.TryGetFileContentAtPath (image, out var file) && file.Etag == etag) {
|
|
// Already published
|
|
}
|
|
else {
|
|
Xamarin.Forms.PageExtensions.Ui.PublishFile (image, data, etag, "image");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (image == null) {
|
|
System.Diagnostics.Debug.WriteLine ("Could not load image: {0}", streamsource);
|
|
}
|
|
return image;
|
|
}
|
|
}
|
|
|
|
public sealed class ImageLoaderSourceHandler : IImageSourceHandler
|
|
{
|
|
public Task<string> LoadImageAsync (ImageSource imagesource, CancellationToken cancelationToken = default (CancellationToken), float scale = 1f)
|
|
{
|
|
var imageLoader = imagesource as UriImageSource;
|
|
return Task.FromResult (imageLoader?.Uri.ToString () ?? "");
|
|
}
|
|
}
|
|
}
|