protected abstract Task <TImageContainer> GenerateImageFromDecoderContainerAsync(IDecodedImage <TDecoderContainer> decoded, ImageInformation imageInformation, bool isPlaceholder);
async Task <TImageContainer> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { using (imageData) { var decoder = ResolveDecoder(imageInformation.Type); var decoderContainer = await decoder.DecodeAsync(imageData, path, source, imageInformation, Parameters); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); if (decoderContainer.IsAnimated) { for (int i = 0; i < decoderContainer.AnimatedImages.Length; i++) { decoderContainer.AnimatedImages[i].Image = await TransformAsync(decoderContainer.AnimatedImages[i].Image, transformations, path, source, isPlaceholder); } } else { decoderContainer.Image = await TransformAsync(decoderContainer.Image, transformations, path, source, isPlaceholder); } } return(await GenerateImageFromDecoderContainerAsync(decoderContainer, imageInformation, isPlaceholder)); } }
public DataResolverResult(TImageContainer imageContainer, LoadingResult loadingResult, ImageInformation imageInformation) { ImageContainer = imageContainer; LoadingResult = loadingResult; ImageInformation = imageInformation; }
public DataResolverResult(IDecodedImage <object> decoded, LoadingResult loadingResult, ImageInformation imageInformation) { Decoded = decoded; LoadingResult = loadingResult; ImageInformation = imageInformation; }
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); }
public async Task RunAsync() { LoadingResult loadingResult = LoadingResult.Failed; bool success = false; try { if (IsCompleted || IsCancelled || ImageService.ExitTasksEarly) { throw new OperationCanceledException(); } ThrowIfCancellationRequested(); // LOAD IMAGE if (!(await TryLoadFromMemoryCacheAsync().ConfigureAwait(false))) { Logger.Debug(string.Format("Generating/retrieving image: {0}", Key)); var resolver = Parameters.CustomDataResolver ?? DataResolverFactory.GetResolver(Parameters.Path, Parameters.Source, Parameters, Configuration); resolver = new WrappedDataResolver(resolver); var imageData = await resolver.Resolve(Parameters.Path, Parameters, CancellationTokenSource.Token).ConfigureAwait(false); loadingResult = imageData.Item2; using (imageData.Item1) { ImageInformation = imageData.Item3; ImageInformation.SetKey(Key, Parameters.CustomCacheKey); ImageInformation.SetPath(Parameters.Path); ThrowIfCancellationRequested(); // Preload if (Parameters.Preload && Parameters.CacheType.HasValue && Parameters.CacheType.Value == CacheType.Disk) { if (loadingResult == LoadingResult.Internet) { Logger?.Debug(string.Format("DownloadOnly success: {0}", Key)); } success = true; return; } ThrowIfCancellationRequested(); var image = await GenerateImageAsync(Parameters.Path, Parameters.Source, imageData.Item1, imageData.Item3, true, false).ConfigureAwait(false); ThrowIfCancellationRequested(); try { BeforeLoading(image, false); if (image != default(TImageContainer) && CanUseMemoryCache) { MemoryCache.Add(Key, imageData.Item3, image); } ThrowIfCancellationRequested(); bool isFadeAnimationEnabled = Parameters.FadeAnimationEnabled ?? Configuration.FadeAnimationEnabled; await SetTargetAsync(image, isFadeAnimationEnabled).ConfigureAwait(false); } finally { AfterLoading(image, false); } } } success = true; } catch (Exception ex) { if (ex is OperationCanceledException || ex is ObjectDisposedException) { if (Configuration.VerboseLoadingCancelledLogging) { Logger.Debug(string.Format("Image loading cancelled: {0}", Key)); } } else { if (_clearCacheOnOutOfMemory && ex is OutOfMemoryException) { MemoryCache.Clear(); } Logger.Error(string.Format("Image loading failed: {0}", Key), ex); if (Configuration.ExecuteCallbacksOnUIThread && Parameters?.OnError != null) { await MainThreadDispatcher.PostAsync(() => { Parameters?.OnError?.Invoke(ex); }).ConfigureAwait(false); } else { Parameters?.OnError?.Invoke(ex); } try { // Error placeholder if enabled if (!Parameters.Preload && !string.IsNullOrWhiteSpace(Parameters.ErrorPlaceholderPath)) { await ShowPlaceholder(Parameters.ErrorPlaceholderPath, KeyForErrorPlaceholder, Parameters.ErrorPlaceholderSource, false).ConfigureAwait(false); } } catch (Exception ex2) { if (!(ex2 is OperationCanceledException)) { Logger.Error(string.Format("Image loading failed: {0}", Key), ex); } } } } finally { try { if (CancellationTokenSource?.IsCancellationRequested == false) { CancellationTokenSource.Cancel(); } } catch (Exception) { } IsCompleted = true; using (Parameters) { if (Configuration.ExecuteCallbacksOnUIThread && Parameters?.OnFinish != null) { await MainThreadDispatcher.PostAsync(() => { if (success) { Parameters?.OnSuccess?.Invoke(ImageInformation, loadingResult); } Parameters?.OnFinish?.Invoke(this); }).ConfigureAwait(false); } else { if (success) { Parameters?.OnSuccess?.Invoke(ImageInformation, loadingResult); } Parameters?.OnFinish?.Invoke(this); } ImageService.RemovePendingTask(this); } } }
protected async override Task <NSImage> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { NSImage 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(); } var nsdata = NSData.FromStream(imageData); imageIn = nsdata.ToImage(new CoreGraphics.CGSize(downsampleWidth, downsampleHeight), ScaleHelper.Scale, Configuration, Parameters, NSDataExtensions.RCTResizeMode.ScaleAspectFill, imageInformation, allowUpscale); } finally { imageData?.Dispose(); } 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 { 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.Dispose(); } } } } finally { _decodingLock.Release(); } } return(imageIn); }
protected async override Task <UIImage> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { UIImage imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { // Special case to handle WebP decoding on iOS if (source != ImageSource.Stream && path.ToLowerInvariant().EndsWith(".webp", StringComparison.InvariantCulture)) { imageIn = new WebP.Touch.WebPCodec().Decode(imageData); } else { var nsdata = NSData.FromStream(imageData); int downsampleWidth = Parameters.DownSampleSize?.Item1 ?? 0; int downsampleHeight = Parameters.DownSampleSize?.Item2 ?? 0; if (Parameters.DownSampleUseDipUnits) { downsampleWidth = downsampleWidth.PointsToPixels(); downsampleHeight = downsampleHeight.PointsToPixels(); } imageIn = nsdata.ToImage(new CoreGraphics.CGSize(downsampleWidth, downsampleHeight), ScaleHelper.Scale, NSDataExtensions.RCTResizeMode.ScaleAspectFill, imageInformation); } } finally { imageData?.Dispose(); } ThrowIfCancellationRequested(); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); await _decodingLock.WaitAsync().ConfigureAwait(false); // Applying transformations is both CPU and memory intensive try { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { var bitmapHolder = transformation.Transform(new BitmapHolder(imageIn)); 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.Dispose(); } } } } finally { _decodingLock.Release(); } } return(imageIn); }
public static WithLoadingResult <T> Encapsulate <T>(T item, LoadingResult result, ImageInformation imageInformation) where T : class { return(new WithLoadingResult <T>(item, result, imageInformation)); }
public DataResolverResult(Stream stream, LoadingResult loadingResult, ImageInformation imageInformation) { Stream = stream; LoadingResult = loadingResult; ImageInformation = imageInformation; }
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)); }
protected override Task <SharedEvasImage> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); TaskCompletionSource <SharedEvasImage> tcs = new TaskCompletionSource <SharedEvasImage>(); MainThreadDispatcher.PostAsync(() => { SharedEvasImage img = new SharedEvasImage(MainWindow); img.IsFilled = true; img.Show(); img.SetStream(imageData); imageData.TryDispose(); img.AddRef(); EcoreMainloop.AddTimer(1.0, () => { img.RemoveRef(); return(false); }); imageInformation.SetOriginalSize(img.Size.Width, img.Size.Height); imageInformation.SetCurrentSize(img.Size.Width, img.Size.Height); // DOWNSAMPLE if (Parameters.DownSampleSize != null && (Parameters.DownSampleSize.Item1 > 0 || Parameters.DownSampleSize.Item2 > 0)) { // Calculate inSampleSize int downsampleWidth = Parameters.DownSampleSize.Item1; int downsampleHeight = Parameters.DownSampleSize.Item2; if (Parameters.DownSampleUseDipUnits) { downsampleWidth = DpiToPixels(downsampleWidth); downsampleHeight = DpiToPixels(downsampleHeight); } int scaleDownFactor = CalculateScaleDownFactor(img.Size.Width, img.Size.Height, downsampleWidth, downsampleHeight); if (scaleDownFactor > 1) { //System.//Console.WriteLine("GenerateImageAsync:: DownSample with {0}", scaleDownFactor); imageInformation.SetCurrentSize( (int)((double)img.Size.Width / scaleDownFactor), (int)((double)img.Size.Height / scaleDownFactor)); EvasInterop.evas_object_image_load_scale_down_set(img.RealHandle, scaleDownFactor); } } tcs.SetResult(img); }); return(tcs.Task); }
protected override Task <MockBitmap> GenerateImageFromDecoderContainerAsync(IDecodedImage <MockBitmap> decoded, ImageInformation imageInformation, bool isPlaceholder) { return(Task.FromResult(new MockBitmap())); }
public ImageLoaderTask(IMemoryCache <TImageContainer> memoryCache, IDataResolverFactory dataResolverFactory, ITarget <TImageContainer, TImageView> target, TaskParameter parameters, IImageService imageService, Configuration configuration, IMainThreadDispatcher mainThreadDispatcher, bool clearCacheOnOutOfMemory) { _clearCacheOnOutOfMemory = clearCacheOnOutOfMemory; MemoryCache = memoryCache; DataResolverFactory = dataResolverFactory; PlatformTarget = target; ImageService = imageService; Configuration = configuration; MainThreadDispatcher = mainThreadDispatcher; Parameters = parameters; CancellationTokenSource = new CancellationTokenSource(); ImageInformation = new ImageInformation(); CanUseMemoryCache = true; KeyRaw = Parameters.Path; if (Parameters.Source == ImageSource.Stream) { CanUseMemoryCache = false; KeyRaw = string.Concat("Stream_", GetNextStreamIndex()); } if (!string.IsNullOrWhiteSpace(Parameters.CustomCacheKey)) { CanUseMemoryCache = true; KeyRaw = Parameters.CustomCacheKey; } if (string.IsNullOrWhiteSpace(KeyRaw)) { KeyRaw = Guid.NewGuid().ToString("N"); } KeyDownsamplingOnly = string.Empty; if (Parameters.DownSampleSize != null && (Parameters.DownSampleSize.Item1 > 0 || Parameters.DownSampleSize.Item2 > 0)) { KeyDownsamplingOnly = string.Concat(";", Parameters.DownSampleSize.Item1, "x", Parameters.DownSampleSize.Item2); } KeyTransformationsOnly = string.Empty; if (Parameters.Transformations != null && Parameters.Transformations.Count > 0) { KeyTransformationsOnly = string.Concat(string.Join(";", Parameters.Transformations.Select(t => t.Key))); } Key = string.Concat(KeyRaw, KeyDownsamplingOnly, KeyTransformationsOnly); KeyWithoutTransformations = string.Concat(KeyRaw, KeyDownsamplingOnly); if (!string.IsNullOrWhiteSpace(Parameters.LoadingPlaceholderPath)) { if (TransformPlaceholders) { KeyForLoadingPlaceholder = string.Concat(Parameters.LoadingPlaceholderPath, KeyDownsamplingOnly, KeyTransformationsOnly); } else { KeyForLoadingPlaceholder = string.Concat(Parameters.LoadingPlaceholderPath, KeyDownsamplingOnly); } } if (!string.IsNullOrWhiteSpace(Parameters.ErrorPlaceholderPath)) { if (TransformPlaceholders) { KeyForErrorPlaceholder = string.Concat(Parameters.ErrorPlaceholderPath, KeyDownsamplingOnly, KeyTransformationsOnly); } else { KeyForErrorPlaceholder = string.Concat(Parameters.ErrorPlaceholderPath, KeyDownsamplingOnly); } } ImageInformation.SetKey(Key, Parameters.CustomCacheKey); ImageInformation.SetPath(Parameters.Path); Target?.SetImageLoadingTask(this); }
protected virtual async Task <TImageContainer> GenerateImageAsync(string path, ImageSource source, IDecodedImage <object> decoded, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { var decoderContainer = new DecodedImage <TDecoderContainer>() { IsAnimated = decoded.IsAnimated, Image = decoded.Image as TDecoderContainer, AnimatedImages = decoded.AnimatedImages?.Select( v => new AnimatedImage <TDecoderContainer>() { Delay = v.Delay, Image = v.Image as TDecoderContainer }) .ToArray() }; if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); if (decoderContainer.IsAnimated) { for (int i = 0; i < decoderContainer.AnimatedImages.Length; i++) { decoderContainer.AnimatedImages[i].Image = await TransformAsync(decoderContainer.AnimatedImages[i].Image, transformations, path, source, isPlaceholder); } } else { decoderContainer.Image = await TransformAsync(decoderContainer.Image, transformations, path, source, isPlaceholder); } } return(await GenerateImageFromDecoderContainerAsync(decoderContainer, imageInformation, isPlaceholder)); }
protected override Task <SharedEvasImage> GenerateImageFromDecoderContainerAsync(IDecodedImage <SharedEvasImage> decoded, ImageInformation imageInformation, bool isPlaceholder) { if (decoded.IsAnimated) { throw new NotImplementedException(); } else { return(Task.FromResult(decoded.Image)); } }
protected async override Task <WriteableBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { BitmapHolder imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { // Special case to handle WebP decoding on Windows if (source != ImageSource.Stream && path.ToLowerInvariant().EndsWith(".webp", StringComparison.OrdinalIgnoreCase)) { throw new NotImplementedException("Webp is not implemented on Windows"); } else if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { imageIn = await imageData.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, imageInformation).ConfigureAwait(false); } else { return(await imageData.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, imageInformation).ConfigureAwait(false)); } } finally { imageData?.Dispose(); } ThrowIfCancellationRequested(); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); await _decodingLock.WaitAsync().ConfigureAwait(false); // Applying transformations is both CPU and memory intensive try { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { IBitmap bitmapHolder = transformation.Transform(imageIn); imageIn = bitmapHolder.ToNative(); } catch (Exception ex) { Logger.Error(string.Format("Transformation failed: {0}", transformation.Key), ex); throw; } finally { if (old != null && old != imageIn && old.Pixels != imageIn.Pixels) { old.FreePixels(); old = null; } } } } finally { _decodingLock.Release(); } } try { return(await imageIn.ToBitmapImageAsync()); } finally { imageIn.FreePixels(); imageIn = null; } }
protected virtual async Task <WithLoadingResult <UIImage> > GetImageAsync(string sourcePath, ImageSource source, bool isPlaceholder, Stream originalStream = null) { if (IsCancelled) { return(new WithLoadingResult <UIImage>(LoadingResult.Canceled)); } LoadingResult?result = null; UIImage image = null; byte[] bytes = null; string path = sourcePath; ImageInformation imageInformation = null; try { if (originalStream != null) { try { // check is stream is memorystream var ms = originalStream as MemoryStream; if (ms != null) { bytes = ms.ToArray(); } else if (originalStream.CanSeek) { bytes = new byte[originalStream.Length]; await originalStream.ReadAsync(bytes, 0, (int)originalStream.Length, CancellationToken.Token).ConfigureAwait(false); } else { using (var ms2 = new MemoryStream()) { await originalStream.CopyToAsync(ms2).ConfigureAwait(false); bytes = ms2.ToArray(); } } path = sourcePath; result = LoadingResult.Stream; } finally { originalStream.Dispose(); } } else { using (var resolver = DataResolverFactory.GetResolver(source, Parameters, DownloadCache, MainThreadDispatcher)) { var data = await resolver.GetData(path, CancellationToken.Token).ConfigureAwait(false); if (data == null) { return(new WithLoadingResult <UIImage>(LoadingResult.Failed)); } image = data.Image; bytes = data.Data; path = data.ResultIdentifier; result = data.Result; imageInformation = data.ImageInformation; } } } catch (OperationCanceledException) { Logger.Debug(string.Format("Image request for {0} got cancelled.", path)); return(new WithLoadingResult <UIImage>(LoadingResult.Canceled)); } catch (Exception ex) { var message = String.Format("Unable to retrieve image data from source: {0}", sourcePath); Logger.Error(message, ex); Parameters.OnError(ex); return(new WithLoadingResult <UIImage>(LoadingResult.Failed)); } if (bytes == null && image == null) { if (result != null && (int)result < 0) // it's below zero if it's an error { return(new WithLoadingResult <UIImage>(result.Value)); } else { return(new WithLoadingResult <UIImage>(LoadingResult.Failed)); } } if (IsCancelled) { return(new WithLoadingResult <UIImage>(LoadingResult.Canceled)); } UIImage imageIn = image; NSData nsdata = null; if (imageIn == null) { // Special case to handle WebP decoding on iOS if (sourcePath.ToLowerInvariant().EndsWith(".webp", StringComparison.InvariantCulture)) { imageIn = new WebP.Touch.WebPCodec().Decode(bytes); } else { // nfloat scale = _imageScale >= 1 ? _imageScale : ScaleHelper.Scale; nsdata = NSData.FromArray(bytes); if (nsdata == null) { return(new WithLoadingResult <UIImage>(LoadingResult.Failed)); } } } bytes = null; // Setting image informations if (imageInformation == null) { imageInformation = new ImageInformation(); } imageInformation.SetCacheKey(path == "Stream" ? GetKey() : GetKey(sourcePath)); // We rely on ImageIO for all datasources except AssetCatalog, this way we don't generate temporary UIImage // furthermore we can do all the work in a thread safe way and in threadpool if (nsdata != null) { int downsampleWidth = Parameters.DownSampleSize?.Item1 ?? 0; int downsampleHeight = Parameters.DownSampleSize?.Item2 ?? 0; if (Parameters.DownSampleUseDipUnits) { downsampleWidth = downsampleWidth.PointsToPixels(); downsampleHeight = downsampleHeight.PointsToPixels(); } imageIn = nsdata.ToImage(new CoreGraphics.CGSize(downsampleWidth, downsampleHeight), _imageScale, NSDataExtensions.RCTResizeMode.ScaleAspectFill, imageInformation); } else if (Parameters.DownSampleSize != null && imageIn != null) { // if we already have the UIImage in memory it doesn't really matter to resize it // furthermore this will only happen for AssetCatalog images (yet) } bool transformPlaceholdersEnabled = Parameters.TransformPlaceholdersEnabled.HasValue ? Parameters.TransformPlaceholdersEnabled.Value : ImageService.Instance.Config.TransformPlaceholders; if (Parameters.Transformations != null && Parameters.Transformations.Count > 0 && (!isPlaceholder || (isPlaceholder && transformPlaceholdersEnabled))) { foreach (var transformation in Parameters.Transformations.ToList() /* to prevent concurrency issues */) { if (IsCancelled) { return(new WithLoadingResult <UIImage>(LoadingResult.Canceled)); } try { var old = imageIn; var bitmapHolder = transformation.Transform(new BitmapHolder(imageIn)); imageIn = bitmapHolder.ToNative(); // Transformation succeeded, so garbage the source if (old != null && old != imageIn && old.Handle != imageIn.Handle) { old.Dispose(); } } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } } return(WithLoadingResult.Encapsulate(imageIn, result.Value, imageInformation)); }
protected override async Task <BitmapSource> GenerateImageFromDecoderContainerAsync(IDecodedImage <BitmapHolder> decoded, ImageInformation imageInformation, bool isPlaceholder) { if (decoded.IsAnimated) { throw new NotImplementedException(); } else { try { if (decoded.Image.HasWriteableBitmap) { return(decoded.Image.WriteableBitmap); } return(await decoded.Image.ToBitmapImageAsync().ConfigureAwait(false)); } finally { decoded.Image.FreePixels(); decoded.Image = null; } } }
protected abstract Task <TImageContainer> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder);
protected async override Task <WriteableBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { BitmapHolder imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { bool allowUpscale = Parameters.AllowUpscale ?? Configuration.AllowUpscale; if (source != ImageSource.Stream && imageInformation.Type == ImageInformation.ImageType.WEBP) { throw new NotImplementedException("Webp is not implemented on Windows"); } else if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { imageIn = await imageData.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false); } else { return(await imageData.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false)); } } 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 { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { IBitmap bitmapHolder = transformation.Transform(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.PixelData != imageIn.PixelData) { old.FreePixels(); old = null; } } } } finally { _decodingLock.Release(); } } try { return(await imageIn.ToBitmapImageAsync()); } finally { imageIn.FreePixels(); imageIn = null; } }
void SetKeys() { KeyRaw = Parameters.Path; if (Parameters.Source == ImageSource.Stream) { if (!string.IsNullOrWhiteSpace(Parameters.StreamChecksum)) { CanUseMemoryCache = true; KeyRaw = Parameters.StreamChecksum; } else { CanUseMemoryCache = false; KeyRaw = string.Concat("Stream_", Guid.NewGuid().ToString("N")); } } if (!string.IsNullOrWhiteSpace(Parameters.CustomCacheKey)) { CanUseMemoryCache = true; KeyRaw = Parameters.CustomCacheKey; } if (Parameters.CacheType == CacheType.Disk) { CanUseMemoryCache = false; } if (string.IsNullOrWhiteSpace(KeyRaw)) { KeyRaw = Guid.NewGuid().ToString("N"); } var vect = Parameters.CustomDataResolver as IVectorDataResolver; if (vect != null) { if (vect.ReplaceStringMap == null || vect.ReplaceStringMap.Count == 0) { KeyRaw = string.Format("{0};(size={1}x{2},dip={3})", KeyRaw, vect.VectorWidth, vect.VectorHeight, vect.UseDipUnits); } else { KeyRaw = string.Format("{0};(size={1}x{2},dip={3},replace=({4}))", KeyRaw, vect.VectorWidth, vect.VectorHeight, vect.UseDipUnits, string.Join(",", vect.ReplaceStringMap.Select(x => string.Format("{0}/{1}", x.Key, x.Value)).OrderBy(v => v))); } } KeyDownsamplingOnly = string.Empty; if (Parameters.DownSampleSize != null && (Parameters.DownSampleSize.Item1 > 0 || Parameters.DownSampleSize.Item2 > 0)) { if (Parameters.DownSampleUseDipUnits) { KeyDownsamplingOnly = string.Concat(";", DpiToPixels(Parameters.DownSampleSize.Item1), "x", DpiToPixels(Parameters.DownSampleSize.Item2)); } else { KeyDownsamplingOnly = string.Concat(";", Parameters.DownSampleSize.Item1, "x", Parameters.DownSampleSize.Item2); } } KeyTransformationsOnly = string.Empty; if (Parameters.Transformations != null && Parameters.Transformations.Count > 0) { KeyTransformationsOnly = string.Concat(";", string.Join(";", Parameters.Transformations.Select(t => t.Key))); } Key = string.Concat(KeyRaw, KeyDownsamplingOnly, KeyTransformationsOnly); KeyWithoutTransformations = string.Concat(KeyRaw, KeyDownsamplingOnly); if (!string.IsNullOrWhiteSpace(Parameters.LoadingPlaceholderPath)) { if (TransformPlaceholders) { KeyForLoadingPlaceholder = string.Concat(Parameters.LoadingPlaceholderPath, KeyDownsamplingOnly, KeyTransformationsOnly); } else { KeyForLoadingPlaceholder = string.Concat(Parameters.LoadingPlaceholderPath, KeyDownsamplingOnly); } var vectLo = Parameters.CustomLoadingPlaceholderDataResolver as IVectorDataResolver; if (vectLo != null) { if (vectLo.ReplaceStringMap == null || vectLo.ReplaceStringMap.Count == 0) { KeyForLoadingPlaceholder = string.Format("{0};(size={1}x{2},dip={3})", KeyForLoadingPlaceholder, vectLo.VectorWidth, vectLo.VectorHeight, vectLo.UseDipUnits); } else { KeyForLoadingPlaceholder = string.Format("{0};(size={1}x{2},dip={3},replace=({4}))", KeyForLoadingPlaceholder, vectLo.VectorWidth, vectLo.VectorHeight, vectLo.UseDipUnits, string.Join(",", vectLo.ReplaceStringMap.Select(x => string.Format("{0}/{1}", x.Key, x.Value)).OrderBy(v => v))); } } } if (!string.IsNullOrWhiteSpace(Parameters.ErrorPlaceholderPath)) { if (TransformPlaceholders) { KeyForErrorPlaceholder = string.Concat(Parameters.ErrorPlaceholderPath, KeyDownsamplingOnly, KeyTransformationsOnly); } else { KeyForErrorPlaceholder = string.Concat(Parameters.ErrorPlaceholderPath, KeyDownsamplingOnly); } var vectEr = Parameters.CustomLoadingPlaceholderDataResolver as IVectorDataResolver; if (vectEr != null) { if (vectEr.ReplaceStringMap == null || vectEr.ReplaceStringMap.Count == 0) { KeyForErrorPlaceholder = string.Format("{0};(size={1}x{2},dip={3})", KeyForErrorPlaceholder, vectEr.VectorWidth, vectEr.VectorHeight, vectEr.UseDipUnits); } else { KeyForErrorPlaceholder = string.Format("{0};(size={1}x{2},dip={3},replace=({4}))", KeyForErrorPlaceholder, vectEr.VectorWidth, vectEr.VectorHeight, vectEr.UseDipUnits, string.Join(",", vectEr.ReplaceStringMap.Select(x => string.Format("{0}/{1}", x.Key, x.Value)).OrderBy(v => v))); } } } ImageInformation.SetKey(Key, Parameters.CustomCacheKey); ImageInformation.SetPath(Parameters.Path); }
protected async override Task <WriteableBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { BitmapHolder imageIn = null; if (imageData == null) { throw new ArgumentNullException(nameof(imageData)); } ThrowIfCancellationRequested(); try { // Special case to handle WebP decoding on Windows string ext = null; if (!string.IsNullOrWhiteSpace(path) && Uri.IsWellFormedUriString(path, UriKind.RelativeOrAbsolute)) { if (source == ImageSource.Url) { ext = Path.GetExtension(new Uri(path).LocalPath).ToLowerInvariant(); } else { ext = Path.GetExtension(path).ToLowerInvariant(); } } bool allowUpscale = Parameters.AllowUpscale ?? Configuration.AllowUpscale; if (source != ImageSource.Stream && ext == ".webp") { throw new NotImplementedException("Webp is not implemented on Windows"); } else if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { imageIn = await imageData.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false); } else { return(await imageData.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, allowUpscale, imageInformation).ConfigureAwait(false)); } } finally { imageData?.Dispose(); } ThrowIfCancellationRequested(); if (enableTransformations && Parameters.Transformations != null && Parameters.Transformations.Count > 0) { var transformations = Parameters.Transformations.ToList(); await _decodingLock.WaitAsync().ConfigureAwait(false); // Applying transformations is both CPU and memory intensive try { foreach (var transformation in transformations) { ThrowIfCancellationRequested(); var old = imageIn; try { IBitmap bitmapHolder = transformation.Transform(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.PixelData != imageIn.PixelData) { old.FreePixels(); old = null; } } } } finally { _decodingLock.Release(); } } try { return(await imageIn.ToBitmapImageAsync()); } finally { imageIn.FreePixels(); imageIn = null; } }
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(); fileOptions.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 (int i = 0; i < decoded.AnimatedImages.Length; i++) { var options = new CGImageDestinationOptions(); options.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)); }
protected override Task <MockBitmap> GenerateImageAsync(string path, ImageSource source, Stream imageData, ImageInformation imageInformation, bool enableTransformations, bool isPlaceholder) { return(Task.FromResult(new MockBitmap())); }