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 virtual async Task <WithLoadingResult <UIImage> > GetImageAsync(string sourcePath, ImageSource source, bool isPlaceholder, Stream originalStream = null) { if (CancellationToken.IsCancellationRequested) { return(null); } UIImage image = null; byte[] bytes = null; string path = sourcePath; LoadingResult?result = null; try { if (originalStream != null) { try { using (var ms = new MemoryStream()) { await originalStream.CopyToAsync(ms).ConfigureAwait(false); bytes = ms.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(null); } image = data.Image; bytes = data.Data; path = data.ResultIdentifier; result = data.Result; } } } catch (System.OperationCanceledException) { Logger.Debug(string.Format("Image request for {0} got cancelled.", path)); return(null); } catch (Exception ex) { var message = String.Format("Unable to retrieve image data from source: {0}", sourcePath); Logger.Error(message, ex); Parameters.OnError(ex); return(null); } if (bytes == null && image == null) { return(null); } var imageToDisplay = await Task.Run(() => { if (CancellationToken.IsCancellationRequested) { return(null); } UIImage imageIn = image; 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 : _screenScale; imageIn = new UIImage(NSData.FromArray(bytes), scale); } } if (Parameters.DownSampleSize != null && ((Parameters.DownSampleSize.Item1 > 0 && imageIn.Size.Width > Parameters.DownSampleSize.Item1) || (Parameters.DownSampleSize.Item2 > 0 && imageIn.Size.Height > Parameters.DownSampleSize.Item2))) { var tempImage = imageIn; imageIn = tempImage.ResizeUIImage(Parameters.DownSampleSize.Item1, Parameters.DownSampleSize.Item2, Parameters.DownSampleInterpolationMode); tempImage.Dispose(); } bool transformPlaceholdersEnabled = Parameters.TransformPlaceholdersEnabled.HasValue ? Parameters.TransformPlaceholdersEnabled.Value : ImageService.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 (CancellationToken.IsCancellationRequested) { return(null); } try { var old = imageIn; var bitmapHolder = transformation.Transform(new BitmapHolder(imageIn)); imageIn = bitmapHolder.ToNative(); // Transformation succeeded, so garbage the source if (old != null) { old.Dispose(); } } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } } return(imageIn); }).ConfigureAwait(false); return(WithLoadingResult.Encapsulate(imageToDisplay, result.Value)); }
protected virtual async Task <WithLoadingResult <WriteableBitmap> > GetImageAsync(string path, ImageSource source, bool isPlaceholder, Stream originalStream = null) { if (IsCancelled) { return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Canceled)); } if (IsCancelled) { return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Canceled)); } Stream stream = null; WithLoadingResult <Stream> streamWithResult; if (originalStream != null) { streamWithResult = new WithLoadingResult <Stream>(originalStream, LoadingResult.Stream); } else { streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); } if (streamWithResult.HasError) { if (streamWithResult.Result == LoadingResult.NotFound) { Logger.Error(string.Format("Not found: {0} from {1}", path, source.ToString())); } return(new WithLoadingResult <WriteableBitmap>(streamWithResult.Result)); } stream = streamWithResult.Item; if (IsCancelled) { return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Canceled)); } try { try { if (stream.Position != 0 && !stream.CanSeek) { if (originalStream != null) { // If we cannot seek the original stream then there's not much we can do return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Failed)); } else { // Assets stream can't be seeked to origin position stream.Dispose(); streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); if (streamWithResult.HasError) { return(new WithLoadingResult <WriteableBitmap>(streamWithResult.Result)); } stream = streamWithResult.Item; } } else { stream.Seek(0, SeekOrigin.Begin); } if (IsCancelled) { return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Canceled)); } } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously retrieving image size from file: " + path, ex); return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Failed)); } WriteableBitmap writableBitmap = null; // Special case to handle WebP decoding if (path.ToLowerInvariant().EndsWith(".webp")) { //TODO Logger.Error("Webp is not implemented on Windows"); return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Failed)); } // Setting image informations var imageInformation = streamWithResult.ImageInformation ?? new ImageInformation(); imageInformation.SetKey(path == "Stream" ? GetKey() : GetKey(path), Parameters.CustomCacheKey); bool transformPlaceholdersEnabled = Parameters.TransformPlaceholdersEnabled.HasValue ? Parameters.TransformPlaceholdersEnabled.Value : ImageService.Instance.Config.TransformPlaceholders; if (Parameters.Transformations != null && Parameters.Transformations.Count > 0 && (!isPlaceholder || (isPlaceholder && transformPlaceholdersEnabled))) { BitmapHolder imageIn = null; try { imageIn = await stream.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, imageInformation).ConfigureAwait(false); } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously loading/decoding image: " + path, ex); return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Failed)); } foreach (var transformation in Parameters.Transformations.ToList() /* to prevent concurrency issues */) { if (IsCancelled) { return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Canceled)); } try { var old = imageIn; IBitmap bitmapHolder = transformation.Transform(imageIn); imageIn = bitmapHolder.ToNative(); if (old != null && old != imageIn && old.Pixels != imageIn.Pixels) { old.FreePixels(); old = null; } } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } writableBitmap = await imageIn.ToBitmapImageAsync(); imageIn.FreePixels(); imageIn = null; } else { try { writableBitmap = await stream.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleUseDipUnits, Parameters.DownSampleInterpolationMode, imageInformation); } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously loading/decoding image: " + path, ex); return(new WithLoadingResult <WriteableBitmap>(LoadingResult.Failed)); } } return(WithLoadingResult.Encapsulate(writableBitmap, streamWithResult.Result, imageInformation)); } finally { if (stream != null) { stream.Dispose(); } } }
protected virtual async Task <WithLoadingResult <SelfDisposingBitmapDrawable> > GetDrawableAsync(string path, ImageSource source, bool isLoadingPlaceHolder, bool isPlaceholder, Stream originalStream = null) { if (IsCancelled) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Canceled)); } // First decode with inJustDecodeBounds=true to check dimensions var options = new BitmapFactory.Options { InJustDecodeBounds = true }; Stream stream = null; WithLoadingResult <Stream> streamWithResult; if (originalStream != null) { streamWithResult = new WithLoadingResult <Stream>(originalStream, LoadingResult.Stream); } else { streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); } if (streamWithResult.HasError) { if (streamWithResult.Result == LoadingResult.NotFound) { Logger.Error(string.Format("Not found: {0} from {1}", path, source.ToString())); } return(new WithLoadingResult <SelfDisposingBitmapDrawable>(streamWithResult.Result)); } stream = streamWithResult.Item; try { try { // NOTE: CURRENTLY NOT NEEDED // if (streamWithResult.Result == LoadingResult.Internet) // { // // When loading from internet stream we shouldn't block otherwise other downloads will be paused // BitmapFactory.DecodeStream(stream, null, options); // } // else // { lock (_decodingLock) { BitmapFactory.DecodeStream(stream, null, options); } // } if (!stream.CanSeek) { if (stream == originalStream) { // If we cannot seek the original stream then there's not much we can do return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Failed)); } else { // Assets stream can't be seeked to origin position stream.Dispose(); streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); if (streamWithResult.HasError) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(streamWithResult.Result)); } stream = streamWithResult.Item; } } else { stream.Seek(0, SeekOrigin.Begin); } } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously retrieving image size from file: " + path, ex); return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Failed)); } if (IsCancelled) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Canceled)); } // Setting image informations var imageInformation = streamWithResult.ImageInformation ?? new ImageInformation(); imageInformation.SetOriginalSize(options.OutWidth, options.OutHeight); imageInformation.SetCurrentSize(options.OutWidth, options.OutHeight); imageInformation.SetCacheKey(path == "Stream" ? GetKey() : GetKey(path)); options.InPurgeable = true; options.InJustDecodeBounds = false; if (!ImageService.Instance.Config.LoadWithTransparencyChannel || Parameters.LoadTransparencyChannel == null || !Parameters.LoadTransparencyChannel.Value) { // Same quality but no transparency channel. This allows to save 50% of memory: 1 pixel=2bytes instead of 4. options.InPreferredConfig = Bitmap.Config.Rgb565; } // CHECK IF BITMAP IS EXIF ROTATED int exifRotation = 0; if (source == ImageSource.Filepath) { exifRotation = path.GetExifRotationDegrees(); } try { 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 image is rotated, swap width/height if (exifRotation == 90 || exifRotation == 270) { downsampleWidth = Parameters.DownSampleSize.Item2; downsampleHeight = Parameters.DownSampleSize.Item1; } if (Parameters.DownSampleUseDipUnits) { downsampleWidth = downsampleWidth.DpToPixels(); downsampleHeight = downsampleHeight.DpToPixels(); } options.InSampleSize = CalculateInSampleSize(options, downsampleWidth, downsampleHeight); if (options.InSampleSize > 1) { imageInformation.SetCurrentSize( (int)((double)options.OutWidth / options.InSampleSize), (int)((double)options.OutHeight / options.InSampleSize)); } // If we're running on Honeycomb or newer, try to use inBitmap if (Utils.HasHoneycomb()) { AddInBitmapOptions(options); } } } catch (Exception ex) { Logger.Error("Something wrong happened while adding decoding options to image: " + path, ex); } if (IsCancelled) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Canceled)); } Bitmap bitmap; try { // NOTE: CURRENTLY NOT NEEDED // if (streamWithResult.Result == LoadingResult.Internet) // { // // When loading from internet stream we shouldn't block otherwise other downloads will be paused // bitmap = BitmapFactory.DecodeStream(stream, null, options); // } // else // { lock (_decodingLock) { bitmap = BitmapFactory.DecodeStream(stream, null, options); } // } } catch (Java.Lang.Throwable vme) { if (vme.Class == Java.Lang.Class.FromType(typeof(Java.Lang.OutOfMemoryError))) { ImageCache.Instance.Clear(); // Clear will also force a Garbage collection } return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Failed)); } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously loading/decoding image: " + path, ex); return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Failed)); } if (bitmap == null) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Failed)); } if (IsCancelled) { return(new WithLoadingResult <SelfDisposingBitmapDrawable>(LoadingResult.Canceled)); } // APPLY EXIF ORIENTATION IF NEEDED if (exifRotation != 0) { bitmap = bitmap.ToRotatedBitmap(exifRotation); } 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 <SelfDisposingBitmapDrawable>(LoadingResult.Canceled)); } try { var old = bitmap; // Applying a transformation is both CPU and memory intensive lock (_decodingLock) { var bitmapHolder = transformation.Transform(new BitmapHolder(bitmap)); bitmap = bitmapHolder.ToNative(); } // Transformation succeeded, so garbage the source if (old != null && old.Handle != IntPtr.Zero && !old.IsRecycled && old != bitmap && old.Handle != bitmap.Handle) { old.Recycle(); old.Dispose(); } } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } } if (isLoadingPlaceHolder) { return(WithLoadingResult.Encapsulate <SelfDisposingBitmapDrawable>( new SelfDisposingAsyncDrawable(Context.Resources, bitmap, this), streamWithResult.Result, imageInformation)); } else { bool isFadeAnimationEnabled = Parameters.FadeAnimationEnabled.HasValue ? Parameters.FadeAnimationEnabled.Value : ImageService.Instance.Config.FadeAnimationEnabled; bool isFadeAnimationEnabledForCached = isFadeAnimationEnabled && (Parameters.FadeAnimationForCachedImages.HasValue ? Parameters.FadeAnimationForCachedImages.Value : ImageService.Instance.Config.FadeAnimationForCachedImages); int fadeDuration = Parameters.FadeAnimationDuration.HasValue ? Parameters.FadeAnimationDuration.Value : ImageService.Instance.Config.FadeAnimationDuration; bool isLocalOrCached = streamWithResult.Result.IsLocalOrCachedResult(); BitmapDrawable placeholderDrawable = null; if (_loadingPlaceholderWeakReference != null) { _loadingPlaceholderWeakReference.TryGetTarget(out placeholderDrawable); } if (isLocalOrCached) { return(WithLoadingResult.Encapsulate <SelfDisposingBitmapDrawable>( new FFBitmapDrawable(Context.Resources, bitmap, placeholderDrawable, fadeDuration, isFadeAnimationEnabled && isFadeAnimationEnabledForCached), streamWithResult.Result, imageInformation)); } return(WithLoadingResult.Encapsulate <SelfDisposingBitmapDrawable>( new FFBitmapDrawable(Context.Resources, bitmap, placeholderDrawable, fadeDuration, isFadeAnimationEnabled), streamWithResult.Result, imageInformation)); } } finally { if (stream != null) { stream.Dispose(); } } }
protected virtual async Task <WithLoadingResult <WriteableBitmap> > GetImageAsync(string sourcePath, ImageSource source, bool isPlaceholder, Stream originalStream = null) { if (CancellationToken.IsCancellationRequested) { return(null); } byte[] bytes = null; string path = sourcePath; LoadingResult?result = null; try { if (originalStream != null) { using (var ms = new MemoryStream()) { await originalStream.CopyToAsync(ms).ConfigureAwait(false); bytes = ms.ToArray(); path = sourcePath; result = LoadingResult.Stream; } } else { using (var resolver = DataResolverFactory.GetResolver(source, Parameters, DownloadCache)) { var data = await resolver.GetData(path, CancellationToken.Token).ConfigureAwait(false); if (data == null) { return(null); } bytes = data.Data; path = data.ResultIdentifier; result = data.Result; } } } catch (OperationCanceledException) { Logger.Debug(string.Format("Image request for {0} got cancelled.", path)); return(null); } catch (Exception ex) { var message = String.Format("Unable to retrieve image data from source: {0}", sourcePath); Logger.Error(message, ex); Parameters.OnError(ex); return(null); } if (bytes == null) { return(null); } var image = await Task.Run(async() => { if (CancellationToken.IsCancellationRequested) { return(null); } WriteableBitmap writableBitmap = null; // Special case to handle WebP decoding if (sourcePath.ToLowerInvariant().EndsWith(".webp")) { //TODO throw new NotImplementedException("Webp is not implemented on Windows"); } bool transformPlaceholdersEnabled = Parameters.TransformPlaceholdersEnabled.HasValue ? Parameters.TransformPlaceholdersEnabled.Value : ImageService.Config.TransformPlaceholders; if (Parameters.Transformations != null && Parameters.Transformations.Count > 0 && (!isPlaceholder || (isPlaceholder && transformPlaceholdersEnabled))) { BitmapHolder imageIn = await bytes.ToBitmapHolderAsync(Parameters.DownSampleSize, Parameters.DownSampleInterpolationMode).ConfigureAwait(false); foreach (var transformation in Parameters.Transformations.ToList() /* to prevent concurrency issues */) { if (CancellationToken.IsCancellationRequested) { return(null); } try { var old = imageIn; IBitmap bitmapHolder = transformation.Transform(imageIn); imageIn = bitmapHolder.ToNative(); // old = null; // PLEASE NOTE! In Windows we could reuse int array (less memory usage) so we don't need that } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } await MainThreadDispatcher.PostAsync(async() => { writableBitmap = await imageIn.ToBitmapImageAsync(); }).ConfigureAwait(false); } else { writableBitmap = await bytes.ToBitmapImageAsync(Parameters.DownSampleSize, Parameters.DownSampleInterpolationMode).ConfigureAwait(false); } return(writableBitmap); }).ConfigureAwait(false); return(WithLoadingResult.Encapsulate(image, result.Value)); }
protected virtual async Task <WithLoadingResult <SelfDisposingBitmapDrawable> > GetDrawableAsync(string path, ImageSource source, bool isLoadingPlaceHolder, bool isPlaceholder, Stream originalStream = null) { if (CancellationToken.IsCancellationRequested) { return(null); } return(await Task.Run <WithLoadingResult <SelfDisposingBitmapDrawable> >(async() => { if (CancellationToken.IsCancellationRequested) { return null; } // First decode with inJustDecodeBounds=true to check dimensions var options = new BitmapFactory.Options { InJustDecodeBounds = true }; Stream stream = null; WithLoadingResult <Stream> streamWithResult = null; if (originalStream != null) { streamWithResult = new WithLoadingResult <Stream>(originalStream, LoadingResult.Stream); } else { streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); } if (streamWithResult == null) { return null; } if (streamWithResult.Item == null) { if (streamWithResult.Result == LoadingResult.NotFound) { Logger.Error(string.Format("Not found: {0} from {1}", path, source.ToString())); } return null; } stream = streamWithResult.Item; try { try { if (streamWithResult.Result == LoadingResult.Internet) { // When loading from internet stream we shouldn't block otherwise other downloads will be paused BitmapFactory.DecodeStream(stream, null, options); } else { lock (_decodingLock) { BitmapFactory.DecodeStream(stream, null, options); } } if (!stream.CanSeek) { if (originalStream != null) { // If we cannot seek the original stream then there's not much we can do return null; } else { // Assets stream can't be seeked to origin position stream.Dispose(); streamWithResult = await GetStreamAsync(path, source).ConfigureAwait(false); stream = streamWithResult == null ? null : streamWithResult.Item; if (stream == null) { return null; } } } else { stream.Seek(0, SeekOrigin.Begin); } } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously retrieving image size from file: " + path, ex); return null; } if (CancellationToken.IsCancellationRequested) { return null; } options.InPurgeable = true; options.InJustDecodeBounds = false; if (!ImageService.Config.LoadWithTransparencyChannel || Parameters.LoadTransparencyChannel == null || !Parameters.LoadTransparencyChannel.Value) { // Same quality but no transparency channel. This allows to save 50% of memory: 1 pixel=2bytes instead of 4. options.InPreferredConfig = Bitmap.Config.Rgb565; } try { 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 = downsampleWidth.DpToPixels(); downsampleHeight = downsampleHeight.DpToPixels(); } options.InSampleSize = CalculateInSampleSize(options, downsampleWidth, downsampleHeight); } // If we're running on Honeycomb or newer, try to use inBitmap if (Utils.HasHoneycomb()) { AddInBitmapOptions(options); } } catch (Exception ex) { Logger.Error("Something wrong happened while adding decoding options to image: " + path, ex); } if (CancellationToken.IsCancellationRequested) { return null; } Bitmap bitmap; try { if (streamWithResult.Result == LoadingResult.Internet) { // When loading from internet stream we shouldn't block otherwise other downloads will be paused bitmap = BitmapFactory.DecodeStream(stream, null, options); } else { lock (_decodingLock) { bitmap = BitmapFactory.DecodeStream(stream, null, options); } } } catch (Java.Lang.Throwable vme) { if (vme.Class == Java.Lang.Class.FromType(typeof(Java.Lang.OutOfMemoryError))) { ImageCache.Instance.Clear(); // Clear will also force a Garbage collection } return null; } catch (Exception ex) { Logger.Error("Something wrong happened while asynchronously loading/decoding image: " + path, ex); return null; } if (bitmap == null || CancellationToken.IsCancellationRequested) { return null; } bool transformPlaceholdersEnabled = Parameters.TransformPlaceholdersEnabled.HasValue ? Parameters.TransformPlaceholdersEnabled.Value : ImageService.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 (CancellationToken.IsCancellationRequested) { return null; } try { var old = bitmap; // Applying a transformation is both CPU and memory intensive lock (_decodingLock) { var bitmapHolder = transformation.Transform(new BitmapHolder(bitmap)); bitmap = bitmapHolder.ToNative(); } // Transformation succeeded, so garbage the source if (old != null && !old.IsRecycled && old != bitmap && old.Handle != bitmap.Handle) { old.Recycle(); old.Dispose(); } } catch (Exception ex) { Logger.Error("Can't apply transformation " + transformation.Key + " to image " + path, ex); } } } if (isLoadingPlaceHolder) { return WithLoadingResult.Encapsulate <SelfDisposingBitmapDrawable>(new SelfDisposingAsyncDrawable(Context.Resources, bitmap, this), streamWithResult.Result); } else { Drawable placeholderDrawable = null; if (_loadingPlaceholderWeakReference != null) { _loadingPlaceholderWeakReference.TryGetTarget(out placeholderDrawable); } return WithLoadingResult.Encapsulate <SelfDisposingBitmapDrawable>(new FFBitmapDrawable(Context.Resources, bitmap, placeholderDrawable, FADE_TRANSITION_MILISECONDS, UseFadeInBitmap), streamWithResult.Result); } } finally { if (stream != null) { stream.Dispose(); } } }).ConfigureAwait(false)); }