diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PerformanceGallery/Scenarios/ImageScenarios.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PerformanceGallery/Scenarios/ImageScenarios.cs index 65a19e064..2e433297e 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PerformanceGallery/Scenarios/ImageScenarios.cs +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/PerformanceGallery/Scenarios/ImageScenarios.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using Xamarin.Forms.Internals; @@ -23,4 +24,50 @@ namespace Xamarin.Forms.Controls.GalleryPages.PerformanceGallery.Scenarios View = new Image { Source = "coffee.png" }; } } + + [Preserve(AllMembers = true)] + internal class ImageScenario3 : PerformanceScenario + { + const int count = 5; + + public ImageScenario3() + : base($"[Image] {count}x AndroidResource") + { + var source = ImageSource.FromFile("bank.png"); + var layout = new StackLayout(); + for (int i = 0; i < count; i++) + { + layout.Children.Add(new Image { Source = source, HeightRequest = 20 }); + } + View = layout; + } + } + + [Preserve(AllMembers = true)] + internal class ImageScenario4 : PerformanceScenario + { + const int count = 5; + static readonly string tempFile; + + static ImageScenario4() + { + //NOTE: copy image to disk in static ctor, so not to interfere with timing + tempFile = Path.Combine(Path.GetTempPath(), $"{nameof(ImageScenario4)}.png"); + using (var embeddedStream = typeof(ImageScenario4).Assembly.GetManifestResourceStream("Xamarin.Forms.Controls.GalleryPages.crimson.jpg")) + using (var fileStream = File.Create(tempFile)) + embeddedStream.CopyTo(fileStream); + } + + public ImageScenario4() + : base($"[Image] {count}x from disk") + { + var source = ImageSource.FromFile(tempFile); + var layout = new StackLayout(); + for (int i = 0; i < count; i++) + { + layout.Children.Add(new Image { Source = source, HeightRequest = 20 }); + } + View = layout; + } + } } diff --git a/Xamarin.Forms.Core/Registrar.cs b/Xamarin.Forms.Core/Registrar.cs index ed6f20890..95c065a64 100644 --- a/Xamarin.Forms.Core/Registrar.cs +++ b/Xamarin.Forms.Core/Registrar.cs @@ -53,17 +53,17 @@ namespace Xamarin.Forms.Internals return (TRegistrable)DependencyResolver.ResolveOrCreate(handlerType, args); } - public TOut GetHandler(Type type) where TOut : TRegistrable + public TOut GetHandler(Type type) where TOut : class, TRegistrable { - return (TOut)GetHandler(type); + return GetHandler(type) as TOut; } - public TOut GetHandler(Type type, params object[] args) where TOut : TRegistrable + public TOut GetHandler(Type type, params object[] args) where TOut : class, TRegistrable { - return (TOut)GetHandler(type, args); + return GetHandler(type, args) as TOut; } - public TOut GetHandlerForObject(object obj) where TOut : TRegistrable + public TOut GetHandlerForObject(object obj) where TOut : class, TRegistrable { if (obj == null) throw new ArgumentNullException(nameof(obj)); @@ -71,10 +71,10 @@ namespace Xamarin.Forms.Internals var reflectableType = obj as IReflectableType; var type = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : obj.GetType(); - return (TOut)GetHandler(type); + return GetHandler(type) as TOut; } - public TOut GetHandlerForObject(object obj, params object[] args) where TOut : TRegistrable + public TOut GetHandlerForObject(object obj, params object[] args) where TOut : class, TRegistrable { if (obj == null) throw new ArgumentNullException(nameof(obj)); @@ -82,7 +82,7 @@ namespace Xamarin.Forms.Internals var reflectableType = obj as IReflectableType; var type = reflectableType != null ? reflectableType.GetTypeInfo().AsType() : obj.GetType(); - return (TOut)GetHandler(type, args); + return GetHandler(type, args) as TOut; } public Type GetHandlerType(Type viewType) diff --git a/Xamarin.Forms.Platform.Android/Extensions/ImageViewExtensions.cs b/Xamarin.Forms.Platform.Android/Extensions/ImageViewExtensions.cs index a0d7968dd..9be2e2631 100644 --- a/Xamarin.Forms.Platform.Android/Extensions/ImageViewExtensions.cs +++ b/Xamarin.Forms.Platform.Android/Extensions/ImageViewExtensions.cs @@ -31,23 +31,30 @@ namespace Xamarin.Forms.Platform.Android imageView.SetImageResource(global::Android.Resource.Color.Transparent); + bool setByImageViewHandler = false; Bitmap bitmap = null; - Drawable drawable = null; - IImageSourceHandler handler; - - if (source != null && (handler = Internals.Registrar.Registered.GetHandlerForObject(source)) != null) + if (source != null) { - if (handler is FileImageSourceHandler) - { - drawable = imageView.Context.GetDrawable((FileImageSource)source); - } - - if (drawable == null) + var imageViewHandler = Internals.Registrar.Registered.GetHandlerForObject(source); + if (imageViewHandler != null) { try { - bitmap = await handler.LoadImageAsync(source, imageView.Context); + await imageViewHandler.LoadImageAsync(source, imageView); + setByImageViewHandler = true; + } + catch (TaskCanceledException) + { + imageController?.SetIsLoading(false); + } + } + else + { + var imageSourceHandler = Internals.Registrar.Registered.GetHandlerForObject(source); + try + { + bitmap = await imageSourceHandler.LoadImageAsync(source, imageView.Context); } catch (TaskCanceledException) { @@ -63,12 +70,10 @@ namespace Xamarin.Forms.Platform.Android return; } - if (!imageView.IsDisposed()) + if (!setByImageViewHandler && !imageView.IsDisposed()) { - if (bitmap == null && drawable != null) - { - imageView.SetImageDrawable(drawable); - } + if (bitmap == null && source is FileImageSource) + imageView.SetImageResource(ResourceManager.GetDrawableByName(((FileImageSource)source).File)); else { imageView.SetImageBitmap(bitmap); diff --git a/Xamarin.Forms.Platform.Android/Renderers/FileImageSourceHandler.cs b/Xamarin.Forms.Platform.Android/Renderers/FileImageSourceHandler.cs index 95d66fb74..1c8025e7b 100644 --- a/Xamarin.Forms.Platform.Android/Renderers/FileImageSourceHandler.cs +++ b/Xamarin.Forms.Platform.Android/Renderers/FileImageSourceHandler.cs @@ -3,11 +3,13 @@ using System.Threading; using System.Threading.Tasks; using Android.Content; using Android.Graphics; +using Android.Widget; +using Android.Net; using Xamarin.Forms.Internals; namespace Xamarin.Forms.Platform.Android { - public sealed class FileImageSourceHandler : IImageSourceHandler + public sealed class FileImageSourceHandler : IImageSourceHandler, IImageViewHandler { // This is set to true when run under designer context internal static bool DecodeSynchronously { @@ -31,5 +33,28 @@ namespace Xamarin.Forms.Platform.Android return bitmap; } + + public Task LoadImageAsync(ImageSource imagesource, ImageView imageView, CancellationToken cancellationToken = default(CancellationToken)) + { + string file = ((FileImageSource)imagesource).File; + if (File.Exists(file)) + { + var uri = Uri.Parse(file); + if (uri != null) + imageView.SetImageURI(uri); + else + Log.Warning(nameof(FileImageSourceHandler), "Could not find image or image file was invalid: {0}", imagesource); + } + else + { + var drawable = ResourceManager.GetDrawable(imageView.Context, file); + if (drawable != null) + imageView.SetImageDrawable(drawable); + else + Log.Warning(nameof(FileImageSourceHandler), "Could not find image or image file was invalid: {0}", imagesource); + } + + return Task.FromResult(true); + } } } \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Renderers/IImageViewHandler.cs b/Xamarin.Forms.Platform.Android/Renderers/IImageViewHandler.cs new file mode 100644 index 000000000..74e342a96 --- /dev/null +++ b/Xamarin.Forms.Platform.Android/Renderers/IImageViewHandler.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System.Threading.Tasks; +using Android.Widget; + +namespace Xamarin.Forms.Platform.Android +{ + /// + /// The successor to IImageSourceHandler, the goal being we can achieve better performance by never creating an Android.Graphics.Bitmap instance + /// + public interface IImageViewHandler : IRegisterable + { + Task LoadImageAsync(ImageSource imagesource, ImageView imageView, CancellationToken cancellationToken = default(CancellationToken)); + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj index 8b6b4ea7e..400b7a56f 100644 --- a/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj +++ b/Xamarin.Forms.Platform.Android/Xamarin.Forms.Platform.Android.csproj @@ -165,6 +165,7 @@ +