/// <summary> /// Creates a new instance of a CloseableStaticBitmap from an existing /// CloseableReference. The CloseableStaticBitmap will hold a reference /// to the bitmap until it's closed. /// </summary> public CloseableStaticBitmap( CloseableReference <SoftwareBitmap> bitmapReference, IQualityInfo qualityInfo, int rotationAngle) { _bitmapReference = Preconditions.CheckNotNull(bitmapReference.CloneOrNull()); _bitmap = _bitmapReference.Get(); _qualityInfo = qualityInfo; _rotationAngle = rotationAngle; }
/// <summary> /// Performs the decode synchronously. /// </summary> private async Task DoDecode(EncodedImage encodedImage, bool isLast) { if (IsFinished() || !EncodedImage.IsValid(encodedImage)) { return; } try { long queueTime = _jobScheduler.GetQueuedTime(); int length = isLast ? encodedImage.Size : GetIntermediateImageEndOffset(encodedImage); if (length == 0) { return; } IQualityInfo quality = isLast ? ImmutableQualityInfo.FULL_QUALITY : GetQualityInfo(); _producerListener.OnProducerStart(_producerContext.Id, PRODUCER_NAME); CloseableImage image = null; try { image = await _parent._imageDecoder .DecodeImageAsync(encodedImage, length, quality, _imageDecodeOptions) .ConfigureAwait(false); } catch (Exception e) { _producerListener.OnProducerFinishWithFailure( _producerContext.Id, PRODUCER_NAME, e, GetExtraMap(image, queueTime, quality, isLast)); HandleError(e); return; } _producerListener.OnProducerFinishWithSuccess( _producerContext.Id, PRODUCER_NAME, GetExtraMap(image, queueTime, quality, isLast)); HandleResult(image, isLast); } finally { EncodedImage.CloseSafely(encodedImage); } }
/// <summary> /// Creates a new instance of a CloseableStaticBitmap. /// </summary> public CloseableStaticBitmap( SoftwareBitmap bitmap, IResourceReleaser <SoftwareBitmap> resourceReleaser, IQualityInfo qualityInfo, int rotationAngle) { _bitmap = Preconditions.CheckNotNull(bitmap); _bitmapReference = CloseableReference <SoftwareBitmap> .of( _bitmap, Preconditions.CheckNotNull(resourceReleaser)); _qualityInfo = qualityInfo; _rotationAngle = rotationAngle; }
private IDictionary <string, string> GetExtraMap( CloseableImage image, long queueTime, IQualityInfo quality, bool isFinal) { if (!_producerListener.RequiresExtraMap(_producerContext.Id)) { return(null); } string queueStr = queueTime.ToString(); string qualityStr = quality.IsOfGoodEnoughQuality.ToString(); string finalStr = isFinal.ToString(); string cacheChoiceStr = _producerContext.ImageRequest.CacheChoice.ToString(); if (image.GetType() == typeof(CloseableStaticBitmap)) { SoftwareBitmap bitmap = ((CloseableStaticBitmap)image).UnderlyingBitmap; string sizeStr = bitmap.PixelWidth + "x" + bitmap.PixelHeight; var extraMap = new Dictionary <string, string>() { { BITMAP_SIZE_KEY, sizeStr }, { JobScheduler.QUEUE_TIME_KEY, queueStr }, { HAS_GOOD_QUALITY_KEY, qualityStr }, { IS_FINAL_KEY, finalStr }, { IMAGE_TYPE_KEY, cacheChoiceStr } }; return(new ReadOnlyDictionary <string, string>(extraMap)); } else { var extraMap = new Dictionary <string, string>() { { JobScheduler.QUEUE_TIME_KEY, queueStr }, { HAS_GOOD_QUALITY_KEY, qualityStr }, { IS_FINAL_KEY, finalStr }, { IMAGE_TYPE_KEY, cacheChoiceStr } }; return(new ReadOnlyDictionary <string, string>(extraMap)); } }
/// <summary> /// Decodes image. /// </summary> /// <param name="encodedImage"> /// Input image (encoded bytes plus meta data). /// </param> /// <param name="length"> /// If image type supports decoding incomplete image then /// determines where the image data should be cut for decoding. /// </param> /// <param name="qualityInfo"> /// Quality information for the image. /// </param> /// <param name="options"> /// Options that cange decode behavior. /// </param> public Task <CloseableImage> DecodeImageAsync( EncodedImage encodedImage, int length, IQualityInfo qualityInfo, ImageDecodeOptions options) { ImageFormat imageFormat = encodedImage.Format; if (imageFormat == ImageFormat.UNINITIALIZED || imageFormat == ImageFormat.UNKNOWN) { imageFormat = ImageFormatChecker.GetImageFormat_WrapIOException( encodedImage.GetInputStream()); encodedImage.Format = imageFormat; } switch (imageFormat) { case ImageFormat.UNKNOWN: throw new ArgumentException("unknown image format"); case ImageFormat.JPEG: return(DecodeJpegAsync(encodedImage, length, qualityInfo) .ContinueWith( task => ((CloseableImage)task.Result), TaskContinuationOptions.ExecuteSynchronously)); case ImageFormat.GIF: return(DecodeGifAsync(encodedImage, options)); case ImageFormat.WEBP_ANIMATED: return(DecodeAnimatedWebpAsync(encodedImage, options)); default: return(DecodeStaticImageAsync(encodedImage) .ContinueWith( task => ((CloseableImage)task.Result), TaskContinuationOptions.ExecuteSynchronously)); } }
/// <summary> /// Decodes a partial jpeg. /// </summary> /// <param name="encodedImage"> /// Input image (encoded bytes plus meta data). /// </param> /// <param name="length"> /// Amount of currently available data in bytes. /// </param> /// <param name="qualityInfo"> /// Quality info for the image. /// </param> /// <returns>A CloseableStaticBitmap.</returns> public Task <CloseableStaticBitmap> DecodeJpegAsync( EncodedImage encodedImage, int length, IQualityInfo qualityInfo) { return(_platformDecoder .DecodeJPEGFromEncodedImageAsync(encodedImage, _bitmapConfig, length) .ContinueWith( task => { try { return new CloseableStaticBitmap( task.Result, qualityInfo, encodedImage.RotationAngle); } finally { task.Result.Dispose(); } }, TaskContinuationOptions.ExecuteSynchronously)); }
protected override void OnNewResultImpl( CloseableReference <CloseableImage> newResult, bool isLast) { // Ignore invalid intermediate results and forward the null result if last if (newResult == null) { if (isLast) { Consumer.OnNewResult(null, true); } return; } // Stateful results cannot be cached and are just forwarded if (newResult.Get().Stateful) { Consumer.OnNewResult(newResult, isLast); return; } // If the intermediate result is not of a better quality than the cached // result, forward the already cached result and don't cache the new result. if (!isLast) { CloseableReference <CloseableImage> currentCachedResult = _memoryCache.Get(_cacheKey); if (currentCachedResult != null) { try { IQualityInfo newInfo = newResult.Get().QualityInfo; IQualityInfo cachedInfo = currentCachedResult.Get().QualityInfo; if (cachedInfo.IsOfFullQuality || cachedInfo.Quality >= newInfo.Quality) { Consumer.OnNewResult(currentCachedResult, false); return; } } finally { CloseableReference <CloseableImage> .CloseSafely(currentCachedResult); } } } // Cache and forward the new result CloseableReference <CloseableImage> newCachedResult = _memoryCache.Cache(_cacheKey, newResult); try { if (isLast) { Consumer.OnProgressUpdate(1f); } Consumer.OnNewResult( (newCachedResult != null) ? newCachedResult : newResult, isLast); } finally { CloseableReference <CloseableImage> .CloseSafely(newCachedResult); } }