Exemple #1
0
        public Task <IBitmap> Load(Stream sourceStream, float?desiredWidth, float?desiredHeight)
        {
            var data = NSData.FromStream(sourceStream);

            var tcs = new TaskCompletionSource <IBitmap>();

            UIApplication.SharedApplication.InvokeOnMainThread(() => {
                try {
#if UIKIT
                    var bitmap = UIImage.LoadFromData(data);
                    if (bitmap == null)
                    {
                        throw new Exception("Failed to load image");
                    }

                    tcs.TrySetResult(new CocoaBitmap(bitmap));
#else
                    tcs.TrySetResult(new CocoaBitmap(new UIImage(data)));
#endif
                } catch (Exception ex) {
                    tcs.TrySetException(ex);
                }
            });
            return(tcs.Task);
        }
Exemple #2
0
        public static PImage ToImage(this NSData data, CGSize destSize, nfloat destScale, Configuration config, TaskParameter parameters, BaseDecoder.RCTResizeMode resizeMode = BaseDecoder.RCTResizeMode.ScaleAspectFit, ImageInformation imageinformation = null, bool allowUpscale = false)
        {
            var decoded = BaseDecoder.SourceRegfToDecodedImage(data, destSize, destScale, config, parameters, resizeMode, imageinformation, allowUpscale);

            PImage result;

            if (decoded.IsAnimated)
            {
#if __IOS__
                result = PImage.CreateAnimatedImage(decoded.AnimatedImages
                                                    .Select(v => v.Image)
                                                    .Where(v => v?.CGImage != null).ToArray(), decoded.AnimatedImages.Sum(v => v.Delay) / 100.0);
#elif __MACOS__
                result = new PImage();
                var repr = decoded.AnimatedImages
                           .Select(v => v.Image.Representations().First())
                           .ToArray();
                result.AddRepresentations(repr);
#endif
            }
            else
            {
                result = decoded.Image;
            }

            return(result);
        }
Exemple #3
0
        public void CIKernel_BasicTest()
        {
            if (!TestRuntime.CheckSystemAndSDKVersion(8, 0))
            {
                Assert.Inconclusive("Custom filters require iOS8+");
            }

            Exception ex = null;
            var       t  = new Thread(() => {
                // This code will crash if an MKMapView has been created previously on
                // the same thread, so just run it on a different thread (MKMapViews can
                // only be created on the main thread). This is obviously an Apple bug,
                // and a radar has been filed: 19249153. ObjC test case: https://github.com/rolfbjarne/CIKernelMKMapViewCrash
                try {
                    PlatformImage uiImg = new PlatformImage(NSBundle.MainBundle.PathForResource("Xam", "png", "CoreImage"));
#if MONOMAC
                    CIImage ciImg     = new CIImage(uiImg.CGImage);
                    CIContext context = new CIContext(null);
#else
                    CIImage ciImg     = new CIImage(uiImg);
                    CIContext context = CIContext.FromOptions(null);
#endif


                    foreach (CustomerFilterType type in Enum.GetValues(typeof(CustomerFilterType)))
                    {
                        MyCustomFilter filter = new MyCustomFilter(type);
                        filter.MyImage        = ciImg;

                        CIImage outputImage = filter.OutputImage;

                        CGImage cgImage = context.CreateCGImage(outputImage, outputImage.Extent);
#if MONOMAC
                        NSImage finalImg = new NSImage(cgImage, new CGSize());
#else
                        UIImage finalImg = new UIImage(cgImage);
#endif
                        Assert.IsNotNull(finalImg, "CIKernel_BasicTest should not be null");
                        Assert.IsTrue(filter.CallbackHit, "CIKernel_BasicTest callback must be hit");
                        if (filter.IsColorKernel)
                        {
                            Assert.IsTrue(filter.kernel is CIColorKernel, "CIKernel_BasicTest we disagree that it is a color kernel");
                        }
                        else
                        {
                            Assert.IsTrue(filter.kernel is CIWarpKernel, "CIKernel_BasicTest we disagree that it is a warp kernel");
                        }
                    }
                } catch (Exception ex2) {
                    ex = ex2;
                }
            });

            t.Start();
            t.Join();
            if (ex != null)
            {
                throw ex;
            }
        }
 protected override Task SetTargetAsync(PImage image, bool animated)
 {
     return(MainThreadDispatcher.PostAsync(() =>
     {
         ThrowIfCancellationRequested();
         PlatformTarget.Set(this, image, animated);
     }));
 }
Exemple #5
0
        public static IBitmap FromNative(this UIImage This, bool copy = false)
        {
            if (copy)
            {
                return(new CocoaBitmap((UIImage)This.Copy()));
            }

            return(new CocoaBitmap(This));
        }
Exemple #6
0
        public static System.IO.Stream AsPngStream(this PImage image)
        {
#if __IOS__ || __TVOS__
            return(image.AsPNG()?.AsStream());
#elif __MACOS__
            var imageRep = new NSBitmapImageRep(image.AsTiff());
            return(imageRep.RepresentationUsingTypeProperties(NSBitmapImageFileType.Png)
                   .AsStream());
#endif
        }
Exemple #7
0
        public static System.IO.Stream AsJpegStream(this PImage image, int quality = 80)
        {
#if __IOS__ || __TVOS__
            return(image.AsJPEG(quality / 100f).AsStream());
#elif __MACOS__
            // todo: jpeg quality?
            var imageRep = new NSBitmapImageRep(image.AsTiff());
            return(imageRep.RepresentationUsingTypeProperties(NSBitmapImageFileType.Jpeg)
                   .AsStream());
#endif
        }
Exemple #8
0
        protected override async Task SetTargetAsync(PImage image, bool animated)
        {
            if (Target == null)
            {
                return;
            }

            await MainThreadDispatcher.PostAsync(() =>
            {
                ThrowIfCancellationRequested();
                PlatformTarget.Set(this, image, animated);
            }).ConfigureAwait(false);
        }
        protected override Task <PImage> GenerateImageFromDecoderContainerAsync(IDecodedImage <PImage> decoded, ImageInformation imageInformation, bool isPlaceholder)
        {
            PImage result;

            if (decoded.IsAnimated)
            {
#if __IOS__
                result = PImage.CreateAnimatedImage(decoded.AnimatedImages
                                                    .Select(v => v.Image)
                                                    .Where(v => v?.CGImage != null).ToArray(), decoded.AnimatedImages.Sum(v => v.Delay) / 100.0);
#elif __MACOS__
                using (var mutableData = NSMutableData.Create())
                {
                    var fileOptions = new CGImageDestinationOptions
                    {
                        GifDictionary = new NSMutableDictionary()
                    };
                    fileOptions.GifDictionary[ImageIO.CGImageProperties.GIFLoopCount] = new NSString("0");
                    //options.GifDictionary[ImageIO.CGImageProperties.GIFHasGlobalColorMap] = new NSString("true");

                    using (var destintation = CGImageDestination.Create(mutableData, MobileCoreServices.UTType.GIF, decoded.AnimatedImages.Length, fileOptions))
                    {
                        for (var i = 0; i < decoded.AnimatedImages.Length; i++)
                        {
                            var options = new CGImageDestinationOptions
                            {
                                GifDictionary = new NSMutableDictionary()
                            };
                            options.GifDictionary[ImageIO.CGImageProperties.GIFUnclampedDelayTime] = new NSString(decoded.AnimatedImages[i].Delay.ToString());
                            destintation.AddImage(decoded.AnimatedImages[i].Image.CGImage, options);
                        }

                        destintation.Close();
                    }

                    result = new PImage(mutableData);

                    // TODO I really don't know why representations count is 1, anyone?
                    // var test = result.Representations();
                }
#endif
            }
            else
            {
                result = decoded.Image;
            }

            return(Task.FromResult(result));
        }
Exemple #10
0
        private async Task <DataResolverResult> ResolveFromNamedResourceAsync(string fileName, string identifier, TaskParameter parameters, CancellationToken token)
        {
            PImage image = null;

            try
            {
#if __IOS__ || __TVOS__
                await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = PImage.FromBundle(identifier)).ConfigureAwait(false);
#elif __MACOS__
                await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = PImage.ImageNamed(identifier)).ConfigureAwait(false);
#endif
            }
            catch { }

            if (image == null && fileName != identifier)
            {
                try
                {
#if __IOS__ || __TVOS__
                    await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = PImage.FromBundle(fileName)).ConfigureAwait(false);
#elif __MACOS__
                    await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = PImage.ImageNamed(fileName)).ConfigureAwait(false);
#endif
                }
                catch { }
            }

            if (image != null)
            {
                token.ThrowIfCancellationRequested();

                var imageInformation = new ImageInformation();
                imageInformation.SetPath(identifier);
                imageInformation.SetFilePath(null);

                var container = new DecodedImage <object>()
                {
                    Image = image
                };

                var result = new DataResolverResult(container, LoadingResult.CompiledResource, imageInformation);

                return(result);
            }

            return(null);
        }
Exemple #11
0
        protected override async Task <PImage> TransformAsync(PImage bitmap, IList <ITransformation> transformations, string path, ImageSource source, bool isPlaceholder)
        {
            await _decodingLock.WaitAsync(CancellationTokenSource.Token).ConfigureAwait(false); // Applying transformations is both CPU and memory intensive

            ThrowIfCancellationRequested();

            try
            {
                foreach (var transformation in transformations)
                {
                    ThrowIfCancellationRequested();

                    var old = bitmap;

                    try
                    {
                        var bitmapHolder = transformation.Transform(new BitmapHolder(bitmap), path, source, isPlaceholder, Key);
                        bitmap = bitmapHolder.ToNative();
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex);
                        throw;
                    }
                    finally
                    {
                        // Transformation succeeded, so garbage the source
                        if (old != null && old.Handle != IntPtr.Zero && old != bitmap && old.Handle != bitmap.Handle)
                        {
                            old.TryDispose();
                        }
                    }
                }
            }
            finally
            {
                _decodingLock.Release();
            }

            return(bitmap);
        }
        protected override Task <PImage> GenerateImageFromDecoderContainerAsync(IDecodedImage <PImage> decoded, ImageInformation imageInformation, bool isPlaceholder)
        {
            PImage result;

            if (decoded.IsAnimated)
            {
#if __IOS__
                result = PImage.CreateAnimatedImage(decoded.AnimatedImages
                                                    .Select(v => v.Image)
                                                    .Where(v => v?.CGImage != null).ToArray(), decoded.AnimatedImages.Sum(v => v.Delay) / 100.0);
#elif __MACOS__
                using (var mutableData = NSMutableData.Create())
                {
                    using (var destintation = CGImageDestination.Create(mutableData, MobileCoreServices.UTType.GIF, decoded.AnimatedImages.Length))
                    {
                        for (int i = 0; i < decoded.AnimatedImages.Length; i++)
                        {
                            var options = new CGImageDestinationOptions();
                            options.GifDictionary = new NSMutableDictionary();
                            options.GifDictionary[ImageIO.CGImageProperties.GIFDelayTime] = NSNumber.FromDouble(decoded.AnimatedImages[i].Delay / 100.0d);

                            destintation.AddImage(decoded.AnimatedImages[i].Image.CGImage, options);
                        }
                        destintation.Close();
                    }

                    // TODO I really don't know why it's not working. Anyone?
                    result = new PImage(mutableData);
                }
#endif
            }
            else
            {
                result = decoded.Image;
            }

            return(Task.FromResult(result));
        }
Exemple #13
0
        public Task <IBitmap> LoadFromResource(string source, float?desiredWidth, float?desiredHeight)
        {
            var tcs = new TaskCompletionSource <IBitmap>();

            UIApplication.SharedApplication.InvokeOnMainThread(() => {
                try {
#if UIKIT
                    var bitmap = UIImage.FromBundle(source);
#else
                    var bitmap = UIImage.ImageNamed(source);
#endif
                    if (bitmap == null)
                    {
                        throw new Exception("Failed to load image from resource: " + source);
                    }

                    tcs.TrySetResult(new CocoaBitmap(bitmap));
                } catch (Exception ex) {
                    tcs.TrySetException(ex);
                }
            });
            return(tcs.Task);
        }
 protected virtual PImage Transform(PImage sourceBitmap)
 {
     return(null);
 }
        public virtual async Task <Tuple <Stream, LoadingResult, ImageInformation> > Resolve(string identifier, TaskParameter parameters, CancellationToken token)
        {
            NSBundle bundle       = null;
            var      filename     = Path.GetFileNameWithoutExtension(identifier);
            var      tmpPath      = Path.GetDirectoryName(identifier).Trim('/');
            var      filenamePath = string.IsNullOrWhiteSpace(tmpPath) ? null : tmpPath + "/";

            foreach (var fileType in fileTypes)
            {
                string file      = null;
                var    extension = Path.HasExtension(identifier) ? Path.GetExtension(identifier) : string.IsNullOrWhiteSpace(fileType) ? string.Empty : "." + fileType;

                token.ThrowIfCancellationRequested();

                int scale = (int)ScaleHelper.Scale;
                if (scale > 1)
                {
                    while (scale > 1)
                    {
                        token.ThrowIfCancellationRequested();

                        var tmpFile = string.Format("{0}@{1}x{2}", filename, scale, extension);
                        bundle = NSBundle._AllBundles.FirstOrDefault(bu =>
                        {
                            var path = string.IsNullOrWhiteSpace(filenamePath) ?
                                       bu.PathForResource(tmpFile, null) :
                                       bu.PathForResource(tmpFile, null, filenamePath);
                            return(!string.IsNullOrWhiteSpace(path));
                        });

                        if (bundle != null)
                        {
                            file = tmpFile;
                            break;
                        }
                        scale--;
                    }
                }

                token.ThrowIfCancellationRequested();

                if (file == null)
                {
                    var tmpFile = string.Format(filename + extension);
                    file   = tmpFile;
                    bundle = NSBundle._AllBundles.FirstOrDefault(bu =>
                    {
                        var path = string.IsNullOrWhiteSpace(filenamePath) ?
                                   bu.PathForResource(tmpFile, null) :
                                   bu.PathForResource(tmpFile, null, filenamePath);

                        return(!string.IsNullOrWhiteSpace(path));
                    });
                }

                token.ThrowIfCancellationRequested();

                if (bundle != null)
                {
                    string path = !string.IsNullOrEmpty(filenamePath) ? bundle.PathForResource(file, null, filenamePath) : bundle.PathForResource(file, null);

                    var stream           = FileStore.GetInputStream(path, true);
                    var imageInformation = new ImageInformation();
                    imageInformation.SetPath(identifier);
                    imageInformation.SetFilePath(path);

                    return(new Tuple <Stream, LoadingResult, ImageInformation>(
                               stream, LoadingResult.CompiledResource, imageInformation));
                }

                token.ThrowIfCancellationRequested();

                if (string.IsNullOrEmpty(fileType))
                {
                    //Asset catalog
                    bool tryAssetsCatalog = true;
#if __IOS__
                    if (!UIDevice.CurrentDevice.CheckSystemVersion(9, 0))
                    {
                        tryAssetsCatalog = false;
                    }
#endif
                    if (tryAssetsCatalog)
                    {
                        NSDataAsset asset = null;

                        try
                        {
                            await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => asset = new NSDataAsset(filename)).ConfigureAwait(false);
                        }
                        catch (Exception) { }

                        if (asset != null)
                        {
                            token.ThrowIfCancellationRequested();
                            var stream           = asset.Data?.AsStream();
                            var imageInformation = new ImageInformation();
                            imageInformation.SetPath(identifier);
                            imageInformation.SetFilePath(null);

                            return(new Tuple <Stream, LoadingResult, ImageInformation>(
                                       stream, LoadingResult.CompiledResource, imageInformation));
                        }
                    }

                    PImage image = null;

                    try
                    {
#if __IOS__
                        await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = PImage.FromBundle(filename)).ConfigureAwait(false);
#elif __MACOS__
                        await ImageService.Instance.Config.MainThreadDispatcher.PostAsync(() => image = NSImage.ImageNamed(filename)).ConfigureAwait(false);
#endif
                    }
                    catch (Exception) { }

                    if (image != null)
                    {
                        token.ThrowIfCancellationRequested();

                        var stream = image.AsPngStream();

                        var imageInformation = new ImageInformation();
                        imageInformation.SetPath(identifier);
                        imageInformation.SetFilePath(null);

                        return(new Tuple <Stream, LoadingResult, ImageInformation>(
                                   stream, LoadingResult.CompiledResource, imageInformation));
                    }
                }
            }

            throw new FileNotFoundException(identifier);
        }
Exemple #16
0
 public static nuint GetMemorySize(this PImage image)
 {
     return((nuint)(image.CGImage.BytesPerRow * image.CGImage.Height));
 }
Exemple #17
0
        protected async override Task <PImage> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder)
        {
            PImage imageIn = null;

            if (imageData == null)
            {
                throw new ArgumentNullException(nameof(imageData));
            }

            ThrowIfCancellationRequested();

            try
            {
                int  downsampleWidth  = Parameters.DownSampleSize?.Item1 ?? 0;
                int  downsampleHeight = Parameters.DownSampleSize?.Item2 ?? 0;
                bool allowUpscale     = Parameters.AllowUpscale ?? Configuration.AllowUpscale;

                if (Parameters.DownSampleUseDipUnits)
                {
                    downsampleWidth  = downsampleWidth.PointsToPixels();
                    downsampleHeight = downsampleHeight.PointsToPixels();
                }

                // Special case to handle WebP decoding on iOS
                if (source != ImageSource.Stream && imageInformation.Type == ImageInformation.ImageType.WEBP)
                {
#if __IOS__
                    await _webpLock.WaitAsync(CancellationTokenSource.Token).ConfigureAwait(false);

                    ThrowIfCancellationRequested();
                    try
                    {
                        var decoder = _webpDecoder as WebP.Touch.WebPCodec;
                        if (decoder == null)
                        {
                            decoder      = new WebP.Touch.WebPCodec();
                            _webpDecoder = decoder;
                        }
                        var decodedWebP = decoder.Decode(imageData);
                        //TODO Add WebP images downsampling!
                        imageIn = decodedWebP;
                    }
                    finally
                    {
                        _webpLock.Release();
                    }
#else
                    throw new NotImplementedException();
#endif
                }
                else
                {
                    var nsdata = NSData.FromStream(imageData);
                    imageIn = nsdata.ToImage(new CoreGraphics.CGSize(downsampleWidth, downsampleHeight), ScaleHelper.Scale, Configuration, Parameters, NSDataExtensions.RCTResizeMode.ScaleAspectFill, imageInformation, allowUpscale);
                }
            }
            finally
            {
                imageData.TryDispose();
            }

            ThrowIfCancellationRequested();

            if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0)
            {
                var transformations = Parameters.Transformations.ToList();

                await _decodingLock.WaitAsync(CancellationTokenSource.Token).ConfigureAwait(false); // Applying transformations is both CPU and memory intensive

                ThrowIfCancellationRequested();

                try
                {
                    bool isAnimation = false;
#if __IOS__
                    if (imageIn.Images != null)
                    {
                        isAnimation = true;
                    }
#endif
                    if (!isAnimation)
                    {
                        foreach (var transformation in transformations)
                        {
                            ThrowIfCancellationRequested();

                            var old = imageIn;

                            try
                            {
                                var bitmapHolder = transformation.Transform(new BitmapHolder(imageIn), path, source, isPlaceholder, Key);
                                imageIn = bitmapHolder.ToNative();
                            }
                            catch (Exception ex)
                            {
                                Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex);
                                throw;
                            }
                            finally
                            {
                                if (old != null && old != imageIn && old.Handle != imageIn.Handle)
                                {
                                    old.TryDispose();
                                }
                            }
                        }
                    }
                    else
                    {
                        // no animted image support for mac implemented
#if __IOS__
                        var animatedImages = imageIn.Images.ToArray();

                        for (int i = 0; i < animatedImages.Length; i++)
                        {
                            var tempImage = animatedImages[i];

                            if (tempImage.CGImage == null)
                            {
                                continue;
                            }

                            foreach (var transformation in transformations)
                            {
                                ThrowIfCancellationRequested();

                                var old = tempImage;

                                try
                                {
                                    var bitmapHolder = transformation.Transform(new BitmapHolder(tempImage), path, source, isPlaceholder, Key);
                                    tempImage = bitmapHolder.ToNative();
                                }
                                catch (Exception ex)
                                {
                                    Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex);
                                    throw;
                                }
                                finally
                                {
                                    if (old != null && old != tempImage && old.Handle != tempImage.Handle)
                                    {
                                        old.TryDispose();
                                    }
                                }
                            }

                            animatedImages[i] = tempImage;
                        }

                        var oldImageIn = imageIn;
                        imageIn = PImage.CreateAnimatedImage(animatedImages.Where(v => v.CGImage != null).ToArray(), imageIn.Duration);
                        oldImageIn.TryDispose();
#endif
                    }
                }
                finally
                {
                    _decodingLock.Release();
                }
            }

            return(imageIn);
        }
Exemple #18
0
 public BitmapHolder(PImage bitmap)
 {
     NativeBitmap = bitmap;
 }
Exemple #19
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CocoaBitmap"/> class.
 /// </summary>
 /// <param name="inner">The native image we are wrapping.</param>
 public CocoaBitmap(UIImage inner)
 {
     _inner = inner;
 }
Exemple #20
0
 protected virtual PImage Transform(PImage sourceBitmap, string path, ImageSource source, bool isPlaceholder, string key)
 {
     return(sourceBitmap);
 }
Exemple #21
0
 public CocoaBitmap(UIImage inner)
 {
     this.inner = inner;
 }
Exemple #22
0
        public static PImage ResizeUIImage(this PImage image, double desiredWidth, double desiredHeight, InterpolationMode interpolationMode)
        {
            var widthRatio  = desiredWidth / image.Size.Width;
            var heightRatio = desiredHeight / image.Size.Height;
            var scaleRatio  = Math.Min(widthRatio, heightRatio);

            if (Math.Abs(desiredWidth) < double.Epsilon)
            {
                scaleRatio = heightRatio;
            }

            if (Math.Abs(desiredHeight) < double.Epsilon)
            {
                scaleRatio = widthRatio;
            }

            var aspectWidth  = image.Size.Width * scaleRatio;
            var aspectHeight = image.Size.Height * scaleRatio;

            var newSize = new CGSize(aspectWidth, aspectHeight);

#if __MACOS__
            var resizedImage = new PImage(newSize);
            resizedImage.LockFocus();
            image.Draw(new CGRect(CGPoint.Empty, newSize), CGRect.Empty, NSCompositingOperation.SourceOver, 1.0f);
            resizedImage.UnlockFocus();
            return(resizedImage);
#elif __IOS__ || __TVOS__
            UIGraphics.BeginImageContextWithOptions(newSize, false, 0);

            try
            {
                image.Draw(new CGRect((nfloat)0.0, (nfloat)0.0, newSize.Width, newSize.Height));

                using (var context = UIGraphics.GetCurrentContext())
                {
                    if (interpolationMode == InterpolationMode.None)
                    {
                        context.InterpolationQuality = CGInterpolationQuality.None;
                    }
                    else if (interpolationMode == InterpolationMode.Low)
                    {
                        context.InterpolationQuality = CGInterpolationQuality.Low;
                    }
                    else if (interpolationMode == InterpolationMode.Medium)
                    {
                        context.InterpolationQuality = CGInterpolationQuality.Medium;
                    }
                    else if (interpolationMode == InterpolationMode.High)
                    {
                        context.InterpolationQuality = CGInterpolationQuality.High;
                    }
                    else
                    {
                        context.InterpolationQuality = CGInterpolationQuality.Low;
                    }

                    var resizedImage = UIGraphics.GetImageFromCurrentImageContext();

                    return(resizedImage);
                }
            }
            finally
            {
                UIGraphics.EndImageContext();
                image.TryDispose();
            }
#endif
        }
Exemple #23
0
 public CocoaBitmap(UIImage inner)
 {
     this.inner = inner;
 }
        public override void Set(IImageLoaderTask task, PImage image, bool animated)
        {
            if (task == null || task.IsCancelled)
            {
                return;
            }

            var control = Control;

            if (control == null)
            {
                return;
            }
#if __MACOS__
            if (control.Image == image)
            {
                return;
            }
#elif __IOS__
            if (control.Image == image && (control.Image?.Images == null || control.Image.Images.Length <= 1))
            {
                return;
            }
#endif
            var parameters = task.Parameters;
            if (animated)
            {
#if __MACOS__
                // no animation support on Mac. NSImageView does not support animation like UIImageView does
                control.Image = image;
#elif __IOS__
                // fade animation
                double fadeDuration = (double)((parameters.FadeAnimationDuration.HasValue ?
                                                parameters.FadeAnimationDuration.Value : ImageService.Instance.Config.FadeAnimationDuration)) / 1000;

                UIView.Transition(control, fadeDuration,
                                  UIViewAnimationOptions.TransitionCrossDissolve
                                  | UIViewAnimationOptions.BeginFromCurrentState,
                                  () =>
                {
                    if (control.Image?.Images != null && control.Image.Images.Length > 1)
                    {
                        control.Image = null;
                    }

                    control.Image = image;
                },
                                  () => { });
#endif
            }
            else
            {
#if __IOS__
                if (control.Image?.Images != null && control.Image.Images.Length > 1)
                {
                    control.Image = null;
                }
#endif
                control.Image = image;
            }
        }