Example #1
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);
            }
        }