/// /// Ensure that the underlying decoder is created /// private void EnsureDecoder() { if (_realDecoder == null) { _realDecoder = BitmapDecoder.CreateFromUriOrStream( _baseUri, _uri, _stream, _createOptions & ~BitmapCreateOptions.DelayCreation, _cacheOption, _requestCachePolicy, true ); // Check to see if someone already got the frames // If so, we need to ensure that the real decoder // references the same frame as the one we already created // Creating a new object would be bad. if (_readOnlyFrames != null) { _realDecoder.SetupFrames(null, _readOnlyFrames); // // The frames have been transfered to the real decoder, so we no // longer need them. // _readOnlyFrames = null; _frames = null; } } }
/// <summary> /// Create BitmapFrame from the uri or stream /// </summary> internal static BitmapFrame CreateFromUriOrStream( Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy ) { // Create a decoder and return the first frame if (uri != null) { Debug.Assert((stream == null), "Both stream and uri are non-null"); BitmapDecoder decoder = BitmapDecoder.CreateFromUriOrStream( baseUri, uri, null, createOptions, cacheOption, uriCachePolicy, true ); if (decoder.Frames.Count == 0) { throw new System.ArgumentException(SR.Get(SRID.Image_NoDecodeFrames), "uri"); } return(decoder.Frames[0]); } else { Debug.Assert((stream != null), "Both stream and uri are null"); BitmapDecoder decoder = BitmapDecoder.Create( stream, createOptions, cacheOption ); if (decoder.Frames.Count == 0) { throw new System.ArgumentException(SR.Get(SRID.Image_NoDecodeFrames), "stream"); } return(decoder.Frames[0]); } }
internal override void FinalizeCreation() { _bitmapInit.EnsureInitializedComplete(); Uri uri = UriSource; if (_baseUri != null) { uri = new Uri(_baseUri, UriSource); } if ((CreateOptions & BitmapCreateOptions.IgnoreImageCache) != 0) { ImagingCache.RemoveFromImageCache(uri); } BitmapImage bitmapImage = CheckCache(uri); if (bitmapImage != null && bitmapImage.CheckAccess() && bitmapImage.SourceRect.Equals(SourceRect) && bitmapImage.DecodePixelWidth == DecodePixelWidth && bitmapImage.DecodePixelHeight == DecodePixelHeight && bitmapImage.Rotation == Rotation && (bitmapImage.CreateOptions & BitmapCreateOptions.IgnoreColorProfile) == (CreateOptions & BitmapCreateOptions.IgnoreColorProfile) ) { _syncObject = bitmapImage.SyncObject; lock (_syncObject) { WicSourceHandle = bitmapImage.WicSourceHandle; IsSourceCached = bitmapImage.IsSourceCached; _convertedDUCEPtr = bitmapImage._convertedDUCEPtr; // // We nee d to keep the strong reference to the cached image for a few reasons: // // The application may release the original cached image and then keep a // reference to this image only, in which case, the cache can be collected. // This will cause a few undesirable results: // 1. The application may choose to decode the same URI again in which case // we will not retrieve it from the cache even though we have a copy already // decoded. // 2. The original cached image holds onto the file stream indirectly which if // collected can cause bad behavior if the entire image is not loaded into // memory. // _cachedBitmapImage = bitmapImage; } UpdateCachedSettings(); return; } BitmapDecoder decoder = null; if (_decoder == null) { // Note: We do not want to insert in the cache if there is a chance that // the decode pixel width/height may cause the decoder LOD to change decoder = BitmapDecoder.CreateFromUriOrStream( _baseUri, UriSource, StreamSource, CreateOptions & ~BitmapCreateOptions.DelayCreation, BitmapCacheOption.None, // do not cache the frames since we will do that here _uriCachePolicy, false ); if (decoder.IsDownloading) { _isDownloading = true; _decoder = decoder; decoder.DownloadProgress += OnDownloadProgress; decoder.DownloadCompleted += OnDownloadCompleted; decoder.DownloadFailed += OnDownloadFailed; } else { Debug.Assert(decoder.SyncObject != null); } } else { // We already had a decoder, meaning we were downloading Debug.Assert(!_decoder.IsDownloading); decoder = _decoder; _decoder = null; } if (decoder.Frames.Count == 0) { throw new System.ArgumentException(SR.Get(SRID.Image_NoDecodeFrames)); } BitmapFrame frame = decoder.Frames[0]; BitmapSource source = frame; Int32Rect sourceRect = SourceRect; if (sourceRect.X == 0 && sourceRect.Y == 0 && sourceRect.Width == source.PixelWidth && sourceRect.Height == source.PixelHeight) { sourceRect = Int32Rect.Empty; } if (!sourceRect.IsEmpty) { CroppedBitmap croppedSource = new CroppedBitmap(); croppedSource.BeginInit(); croppedSource.Source = source; croppedSource.SourceRect = sourceRect; croppedSource.EndInit(); source = croppedSource; if (_isDownloading) { // Unregister the download events because this is a dummy image. See comment below. source.UnregisterDownloadEventSource(); } } int finalWidth = DecodePixelWidth; int finalHeight = DecodePixelHeight; if (finalWidth == 0 && finalHeight == 0) { finalWidth = source.PixelWidth; finalHeight = source.PixelHeight; } else if (finalWidth == 0) { finalWidth = (source.PixelWidth * finalHeight) / source.PixelHeight; } else if (finalHeight == 0) { finalHeight = (source.PixelHeight * finalWidth) / source.PixelWidth; } if (finalWidth != source.PixelWidth || finalHeight != source.PixelHeight || Rotation != Rotation.Rotate0) { TransformedBitmap transformedSource = new TransformedBitmap(); transformedSource.BeginInit(); transformedSource.Source = source; TransformGroup transformGroup = new TransformGroup(); if (finalWidth != source.PixelWidth || finalHeight != source.PixelHeight) { int oldWidth = source.PixelWidth; int oldHeight = source.PixelHeight; Debug.Assert(oldWidth > 0 && oldHeight > 0); transformGroup.Children.Add( new ScaleTransform( (1.0 * finalWidth) / oldWidth, (1.0 * finalHeight) / oldHeight)); } if (Rotation != Rotation.Rotate0) { double rotation = 0.0; switch (Rotation) { case Rotation.Rotate0: rotation = 0.0; break; case Rotation.Rotate90: rotation = 90.0; break; case Rotation.Rotate180: rotation = 180.0; break; case Rotation.Rotate270: rotation = 270.0; break; default: Debug.Assert(false); break; } transformGroup.Children.Add(new RotateTransform(rotation)); } transformedSource.Transform = transformGroup; transformedSource.EndInit(); source = transformedSource; if (_isDownloading) { // // If we're currently downloading, then the BitmapFrameDecode isn't actually // the image, it's just a 1x1 placeholder. The chain we're currently building // will be replaced with another chain once download completes, so there's no // need to have this chain handle DownloadCompleted. // // Having this chain handle DownloadCompleted is actually a bad thing. Because // the dummy is just 1x1, the TransformedBitmap we're building here will have // a large scaling factor (to scale the image from 1x1 up to whatever // DecodePixelWidth/Height specifies). When the TransformedBitmap receives // DownloadCompleted from the BFD, it will call into WIC to create a new // bitmap scaler using the same large scaling factor, which can produce a huge // bitmap (since the BFD is now no longer 1x1). This problem is made worse if // this BitmapImage has BitmapCacheOption.OnLoad, since that will put a // CachedBitmap after the TransformedBitmap. When DownloadCompleted propagates // from the TransformedBitmap down to the CachedBitmap, the CachedBitmap will // call CreateBitmapFromSource using the TransformedBitmap, which calls // CopyPixels on the huge TransformedBitmap. We want to avoid chewing up the // CPU and the memory, so we unregister the download event handlers here. // source.UnregisterDownloadEventSource(); } } // // If the original image has a color profile and IgnoreColorProfile is not one of the create options, // apply the profile so bits are color-corrected. // if ((CreateOptions & BitmapCreateOptions.IgnoreColorProfile) == 0 && frame.ColorContexts != null && frame.ColorContexts[0] != null && frame.ColorContexts[0].IsValid && source.Format.Format != PixelFormatEnum.Extended ) { // NOTE: Never do this for a non-MIL pixel format, because the format converter has // special knowledge to deal with the profile PixelFormat duceFormat = BitmapSource.GetClosestDUCEFormat(source.Format, source.Palette); bool changeFormat = (source.Format != duceFormat); ColorContext destinationColorContext; // We need to make sure, we can actually create the ColorContext for the destination duceFormat // If the duceFormat is gray or scRGB, the following is not supported, so we cannot // create the ColorConvertedBitmap try { destinationColorContext = new ColorContext(duceFormat); } catch (NotSupportedException) { destinationColorContext = null; } if (destinationColorContext != null) { bool conversionSuccess = false; bool badColorContext = false; // First try if the color converter can handle the source format directly // Its possible that the color converter does not support certain pixelformats, so put a try/catch here. try { ColorConvertedBitmap colorConvertedBitmap = new ColorConvertedBitmap( source, frame.ColorContexts[0], destinationColorContext, duceFormat ); source = colorConvertedBitmap; if (_isDownloading) { // Unregister the download events because this is a dummy image. See comment above. source.UnregisterDownloadEventSource(); } conversionSuccess = true; } catch (NotSupportedException) { } catch (FileFormatException) { // If the file contains a bad color context, we catch the exception here // and don't bother trying the color conversion below, since color transform isn't possible // with the given color context. badColorContext = true; } if (!conversionSuccess && !badColorContext && changeFormat) { // If the conversion failed, we first use // a FormatConvertedBitmap, and then Color Convert that one... FormatConvertedBitmap formatConvertedBitmap = new FormatConvertedBitmap(source, duceFormat, source.Palette, 0.0); ColorConvertedBitmap colorConvertedBitmap = new ColorConvertedBitmap( formatConvertedBitmap, frame.ColorContexts[0], destinationColorContext, duceFormat ); source = colorConvertedBitmap; if (_isDownloading) { // Unregister the download events because this is a dummy image. See comment above. source.UnregisterDownloadEventSource(); } } } } if (CacheOption != BitmapCacheOption.None) { try { // The bitmaps bits could be corrupt, and this will cause an exception if the CachedBitmap forces a decode. CachedBitmap cachedSource = new CachedBitmap(source, CreateOptions & ~BitmapCreateOptions.DelayCreation, CacheOption); source = cachedSource; if (_isDownloading) { // Unregister the download events because this is a dummy image. See comment above. source.UnregisterDownloadEventSource(); } } catch (Exception e) { RecoverFromDecodeFailure(e); CreationCompleted = true; // we're bailing out because the decode failed return; } } // If CacheOption == OnLoad, no need to keep the stream around if (decoder != null && CacheOption == BitmapCacheOption.OnLoad) { decoder.CloseStream(); } else if (CacheOption != BitmapCacheOption.OnLoad) { //ensure that we don't GC the source _finalSource = source; } WicSourceHandle = source.WicSourceHandle; IsSourceCached = source.IsSourceCached; CreationCompleted = true; UpdateCachedSettings(); // Only insert in the imaging cache if download is complete if (!IsDownloading) { InsertInCache(uri); } }