Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media / Imaging / BitmapSource.cs / 1458001 / BitmapSource.cs
//------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, All Rights Reserved. // // File: BitmapSource.cs // //----------------------------------------------------------------------------- using System; using System.IO; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Security; using System.Net; using System.Security.Permissions; using System.Runtime.InteropServices; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using MS.Win32; using System.IO.Packaging; using UnsafeNativeMethods = MS.Win32.PresentationCore.UnsafeNativeMethods; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; using MS.Internal.PresentationCore; // SecurityHelper namespace System.Windows.Media.Imaging { #region BitmapSource ////// Interface for Bitmap Sources, included decoders and effects /// [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] public abstract class BitmapSource : ImageSource, DUCE.IResource { #region Constructor ////// Create a BitmapSource from an array of pixels. /// /// Width of the Bitmap /// Height of the Bitmap /// Horizontal DPI of the Bitmap /// Vertical DPI of the Bitmap /// Format of the Bitmap /// Palette of the Bitmap /// Array of pixels /// stride public static BitmapSource Create( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, Imaging.BitmapPalette palette, System.Array pixels, int stride ) { return new CachedBitmap( pixelWidth, pixelHeight, dpiX, dpiY, pixelFormat, palette, pixels, stride); } ////// Create a BitmapSource from an array of pixels in unmanaged memory. /// /// Width of the Bitmap /// Height of the Bitmap /// Horizontal DPI of the Bitmap /// Vertical DPI of the Bitmap /// Format of the Bitmap /// Palette of the Bitmap /// Pointer to the buffer in memory /// Size of the buffer /// stride ////// Callers must have UnmanagedCode permission to call this API. /// ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] unsafe public static BitmapSource Create( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, Imaging.BitmapPalette palette, IntPtr buffer, int bufferSize, int stride ) { SecurityHelper.DemandUnmanagedCode(); return new CachedBitmap( pixelWidth, pixelHeight, dpiX, dpiY, pixelFormat, palette, buffer, bufferSize, stride); } ////// Constructor /// ////// Critical: Accesses _wicSource /// TreatAsSafe: Inputs are safe /// [SecurityCritical, SecurityTreatAsSafe] protected BitmapSource() { // Synchronize for *this* object only by default. _syncObject = _bitmapInit; _isSourceCached = false; } ////// Internal Constructor /// /// useVirtuals: Should properties and methods like PixelWidth and CopyPixels use their "default" implementation. /// ////// Critical: Accesses _wicSource /// TreatAsSafe: Input (bool) is safe /// [SecurityCritical, SecurityTreatAsSafe] internal BitmapSource(bool useVirtuals) { _useVirtuals = true; _isSourceCached = false; // Synchronize for *this* object only by default. _syncObject = _bitmapInit; } ////// Creates a copy of this object. /// ///The copy. public new BitmapSource Clone() { return (BitmapSource)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed version for convenience. /// ///The copy. public new BitmapSource CloneCurrentValue() { return (BitmapSource)base.CloneCurrentValue(); } #endregion Constructor #region Public properties and methods ////// Native format of the bitmap's data. /// If the BitmapSource is directly readable, this is the format the /// pixels will be in when they are read. /// public virtual System.Windows.Media.PixelFormat Format { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _format; } } ////// Width, in pixels, of the bitmap. /// public virtual int PixelWidth { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _pixelWidth; } } ////// Height, in pixels, of the bitmap. /// public virtual int PixelHeight { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _pixelHeight; } } ////// Horizontal DPI of the bitmap. /// public virtual double DpiX { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _dpiX; } } ////// Vertical DPI of the bitmap. /// public virtual double DpiY { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _dpiY; } } ////// Retrieve and set the bitmap palette. /// public virtual Imaging.BitmapPalette Palette { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); if (_palette == null) { // update the local palette if (_format.Palettized) { _palette = Imaging.BitmapPalette.CreateFromBitmapSource(this); } } return _palette; } } ////// Returns true if the BitmapSource is downloading content /// public virtual bool IsDownloading { get { ReadPreamble(); return false; } } ////// Raised when downloading content is done /// May not be raised for all content. /// public virtual event EventHandler DownloadCompleted { add { WritePreamble(); _downloadEvent.AddEvent(value); } remove { WritePreamble(); _downloadEvent.RemoveEvent(value); } } ////// Raised when download has progressed /// May not be raised for all content. /// public virtual event EventHandlerDownloadProgress { add { WritePreamble(); _progressEvent.AddEvent(value); } remove { WritePreamble(); _progressEvent.RemoveEvent(value); } } /// /// Raised when download has failed /// May not be raised for all content. /// public virtual event EventHandlerDownloadFailed { add { WritePreamble(); _failedEvent.AddEvent(value); } remove { WritePreamble(); _failedEvent.RemoveEvent(value); } } /// /// Raised when decoding has failed /// May not be raised for all content. /// public virtual event EventHandlerDecodeFailed { add { WritePreamble(); EnsureShouldUseVirtuals(); _decodeFailedEvent.AddEvent(value); } remove { WritePreamble(); EnsureShouldUseVirtuals(); _decodeFailedEvent.RemoveEvent(value); } } /// /// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). An empty rect (all 0s) will copy the /// entire bitmap. /// /// Source rect to copy. Int32Rect.Empty specifies the entire rect /// Destination array /// Stride /// Offset in the array to begin copying ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { EnsureShouldUseVirtuals(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CriticalCopyPixels(sourceRect, pixels, stride, offset); } ////// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). /// /// Destination array /// Stride /// Offset to begin at ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Array pixels, int stride, int offset) { Int32Rect sourceRect = Int32Rect.Empty; EnsureShouldUseVirtuals(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CopyPixels(sourceRect, pixels, stride, offset); } ////// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). An empty rect (all 0s) will copy the /// entire bitmap. /// /// Source rect to copy. Int32Rect.Empty specified entire Bitmap /// Pointer to the buffer /// Size of buffer /// Stride ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride) { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CriticalCopyPixels(sourceRect, buffer, bufferSize, stride); } ////// Get the width of the bitmap in measure units (96ths of an inch). /// public override double Width { get { ReadPreamble(); return GetWidthInternal(); } } ////// Get the width of the bitmap in measure units (96ths of an inch). /// public override double Height { get { ReadPreamble(); return GetHeightInternal(); } } ////// Get the Metadata of the bitmap /// public override ImageMetadata Metadata { get { ReadPreamble(); return null; } } #endregion #region Internal, Protected and Private properties and methods ////// Helper function to calculate Width. /// private double GetWidthInternal() { return ImageSource.PixelsToDIPs(this.DpiX, this.PixelWidth); } ////// Helper function to calculate Height. /// private double GetHeightInternal() { return ImageSource.PixelsToDIPs(this.DpiY, this.PixelHeight); } ////// Get the Size for the bitmap /// internal override Size Size { get { ReadPreamble(); return new Size(Math.Max(0, GetWidthInternal()), Math.Max(0, GetHeightInternal())); } } internal bool DelayCreation { get { return _delayCreation; } set { _delayCreation = value; if (_delayCreation) { CreationCompleted = false; } } } internal bool CreationCompleted { get { return _creationComplete; } set { _creationComplete = value; } } /// /// Demand that the bitmap should be created if it was delay-created. /// internal void CompleteDelayedCreation() { // Protect against multithreaded contention on delayed creation. if (DelayCreation) { lock (_syncObject) { if (DelayCreation) { EnsureShouldUseVirtuals(); DelayCreation = false; try { FinalizeCreation(); } catch { DelayCreation = true; throw; } CreationCompleted = true; } } } } internal virtual void FinalizeCreation() { throw new NotImplementedException(); } private void EnsureShouldUseVirtuals() { if (_useVirtuals == false) { throw new NotImplementedException(); } } internal object SyncObject { get { Debug.Assert(_syncObject != null); return _syncObject; } } internal bool IsSourceCached { get { return _isSourceCached; } set { _isSourceCached = value; } } ////// Critical - access safehandle that points to unmanaged resource /// TreatAsSafe - Set: Its safe because any value is OK as the SafeHandle cannot be created /// by an arbitrary IntPtr. /// internal BitmapSourceSafeMILHandle WicSourceHandle { [SecurityCritical] get { CompleteDelayedCreation(); if (_wicSource == null || _wicSource.IsInvalid) { ManagedBitmapSource managedBitmapSource = new ManagedBitmapSource(this); _wicSource = new BitmapSourceSafeMILHandle(Marshal.GetComInterfaceForObject( managedBitmapSource, typeof(System.Windows.Media.Imaging.BitmapSource.IWICBitmapSource))); } return _wicSource; } [SecurityCritical, SecurityTreatAsSafe] set { if (value != null) { IntPtr wicSource = IntPtr.Zero; Guid _uuidWicBitmapSource = MILGuidData.IID_IWICBitmapSource; HRESULT.Check(UnsafeNativeMethods.MILUnknown.QueryInterface( value, ref _uuidWicBitmapSource, out wicSource)); _wicSource = new BitmapSourceSafeMILHandle(wicSource, value); UpdateCachedSettings(); } else { _wicSource = null; } } } /// /// Update local variables from the unmanaged resource /// ////// Critical - access/creates critical resources /// [SecurityCritical] internal virtual void UpdateCachedSettings() { EnsureShouldUseVirtuals(); uint pw, ph; lock (_syncObject) { _format = PixelFormat.GetPixelFormat(_wicSource); HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.GetSize( _wicSource, out pw, out ph)); HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.GetResolution( _wicSource, out _dpiX, out _dpiY)); } _pixelWidth = (int)pw; _pixelHeight = (int)ph; } ////// CriticalCopyPixels /// /// /// /// /// ////// Critical: This code can be used to expose pixel information /// [SecurityCritical] [FriendAccessAllowed] // Built into Core, also used by Framework. unsafe internal void CriticalCopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { ReadPreamble(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); if (pixels == null) throw new System.ArgumentNullException("pixels"); if (pixels.Rank != 1) throw new ArgumentException(SR.Get(SRID.Collection_BadRank), "pixels"); if (offset < 0) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } int elementSize = -1; if (pixels is byte[]) elementSize = 1; else if (pixels is short[] || pixels is ushort[]) elementSize = 2; else if (pixels is int[] || pixels is uint[] || pixels is float[]) elementSize = 4; else if (pixels is double[]) elementSize = 8; if (elementSize == -1) throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); int destBufferSize = checked(elementSize * (pixels.Length - offset)); if (pixels is byte[]) { fixed (void* pixelArray = &((byte[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is short[]) { fixed (void* pixelArray = &((short[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is ushort[]) { fixed (void* pixelArray = &((ushort[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is int[]) { fixed (void* pixelArray = &((int[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is uint[]) { fixed (void* pixelArray = &((uint[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is float[]) { fixed (void* pixelArray = &((float[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is double[]) { fixed (void* pixelArray = &((double[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } } ////// CriticalCopyPixels /// /// /// /// /// ////// Critical: There is a risk of buffer overruns when copying data to the buffer. /// This code can also be used to expose pixel information /// [SecurityCritical] internal void CriticalCopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride) { if (buffer == IntPtr.Zero) throw new ArgumentNullException("buffer"); if (stride <= 0) throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterMustBeGreaterThanZero)); if (sourceRect.Width <= 0) sourceRect.Width = PixelWidth; if (sourceRect.Height <= 0) sourceRect.Height = PixelHeight; if (sourceRect.Width > PixelWidth) throw new ArgumentOutOfRangeException("sourceRect.Width", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelWidth)); if (sourceRect.Height > PixelHeight) throw new ArgumentOutOfRangeException("sourceRect.Height", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelHeight)); int minStride = checked(((sourceRect.Width * Format.BitsPerPixel) + 7) / 8); if (stride < minStride) throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, minStride)); int minRequiredDestSize = checked((stride * (sourceRect.Height - 1)) + minStride); if (bufferSize < minRequiredDestSize) throw new ArgumentOutOfRangeException("buffer", SR.Get(SRID.ParameterCannotBeLessThan, minRequiredDestSize)); lock (_syncObject) { HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.CopyPixels( WicSourceHandle, ref sourceRect, (uint)stride, (uint)bufferSize, buffer )); } } ////// Critical: This code is used to check and grant access to pixel data /// TreatAsSafe: This code does not elevate /// [SecurityCritical, SecurityTreatAsSafe] protected void CheckIfSiteOfOrigin() { string uri = null; // This call is inheritance demand protected. It is overridden in // BitmapFrameDecoder and BitmapImage if (CanSerializeToString()) { // This call returns the URI either as an absolute URI which the user // passed in, in the first place or as the string "image" // we only allow this code to succeed in the case of Uri and if it is site of // origin or pack:. In all other conditions we fail uri = ConvertToString(null, null); } SecurityHelper.DemandMediaAccessPermission(uri); } ////// Called when DUCE resource requires updating /// ////// Critical - access critical resource _convertedDUCEPtr /// TreatAsSafe - All inputs verified /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck) { base.UpdateResource(channel, skipOnChannelCheck); UpdateBitmapSourceResource(channel, skipOnChannelCheck); } ////// Critical - access/create critical resource, returns unmanaged pointer /// internal virtual BitmapSourceSafeMILHandle DUCECompatiblePtr { [SecurityCritical] get { BitmapSourceSafeMILHandle /* IWICBitmapSource */ pIWICSource = WicSourceHandle; BitmapSourceSafeMILHandle /* CWICWrapperBitmap as IWICBitmapSource */ pCWICWrapperBitmap = null; // if we've already cached the ptr, reuse it. if (_convertedDUCEPtr != null && !_convertedDUCEPtr.IsInvalid) { // already in friendly format Debug.Assert(_isSourceCached); } else { bool supportedFormat = (Array.IndexOf(s_supportedDUCEFormats, Format) != -1); if (supportedFormat && _isSourceCached) { #region Make sure the image is decoded on the UI thread // In the case that the source is cached (ie it is already an IWICBitmap), // its possible that the bitmap is a demand bitmap. The demand bitmap only // copies the source bits when absolutely required (ie CopyPixels). This means // that if a decode frame is attached to the demand bitmap, it may decode bits // on the render thread (bad!). To prevent that, we call CopyPixels for the first // pixel which will decode the entire image. // // Ideally, we need an implementation of IWICBitmap that can cache on a scanline // basis as well can be forced to "realize" its cache when requested. Consider // adding a method on IWICBitmap such as RealizeCache(WICRect *). // Int32Rect sourceRect = new Int32Rect(0, 0, 1, 1); PixelFormat format = Format; int bufferSize = (format.BitsPerPixel + 7) / 8; byte[] buffer = new byte[bufferSize]; // If the bitmap has corrupt pixel data, we may not have detected it until now. // At this point the user cannot recover gracefully, so we'll display a 1x1 image // similar to what LateBoundDecoder does before it's done downloading. try { unsafe { fixed (void* pixelArray = &((byte[])buffer)[0]) { HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.CopyPixels( pIWICSource, ref sourceRect, (uint)bufferSize, (uint)bufferSize, (IntPtr)pixelArray )); } } } catch (Exception e) { RecoverFromDecodeFailure(e); // the source will change during recovery, so we need to grab its new value pIWICSource = WicSourceHandle; } #endregion } else // needs caching { BitmapSourceSafeMILHandle pIWicConverter = null; using (FactoryMaker factoryMaker = new FactoryMaker()) { try { if (!supportedFormat) { #region Convert the source to a compatible format that's writable Guid destFmt = GetClosestDUCEFormat(Format, Palette).Guid; // This forces a cached system memory copy of the image in PARGB32 format. This is // necessary to avoid format conversion in the UCE during render and accompanying // sychronization locks with UI thread during bitmap access. HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateFormatConverter( factoryMaker.ImagingFactoryPtr, out pIWicConverter)); HRESULT.Check(UnsafeNativeMethods.WICFormatConverter.Initialize( pIWicConverter, pIWICSource, ref destFmt, DitherType.DitherTypeNone, new SafeMILHandle(IntPtr.Zero), 0, WICPaletteType.WICPaletteTypeCustom)); pIWICSource = pIWicConverter; #endregion } #region Cache the source in memory to ensure it's not decoded/converted on the render thread try { HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( factoryMaker.ImagingFactoryPtr, pIWICSource, WICBitmapCreateCacheOptions.WICBitmapCacheOnLoad, out pIWICSource)); } catch (Exception e) { RecoverFromDecodeFailure(e); // the source will change during recovery, so we need to grab its new value pIWICSource = WicSourceHandle; } _isSourceCached = true; #endregion } finally { if (pIWicConverter != null) pIWicConverter.Close(); } } } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.CreateCWICWrapperBitmap( pIWICSource, out pCWICWrapperBitmap)); UnsafeNativeMethods.MILUnknown.AddRef(pCWICWrapperBitmap); _convertedDUCEPtr = new BitmapSourceSafeMILHandle(pCWICWrapperBitmap.DangerousGetHandle(), pIWICSource); } return _convertedDUCEPtr; } } internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(this, channel, DUCE.ResourceType.TYPE_BITMAPSOURCE)) { UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ ); } return _duceResource.GetHandle(channel); } DUCE.ResourceHandle DUCE.IResource.AddRefOnChannel(DUCE.Channel channel) { // Reconsider the need for this lock when removing the MultiChannelResource. using (CompositionEngineLock.Acquire()) { return AddRefOnChannelCore(channel); } } internal override int GetChannelCountCore() { return _duceResource.GetChannelCount(); } int DUCE.IResource.GetChannelCount() { return GetChannelCountCore(); } internal override DUCE.Channel GetChannelCore(int index) { return _duceResource.GetChannel(index); } DUCE.Channel DUCE.IResource.GetChannel(int index) { return GetChannelCore(index); } ////// SecurityCritical: This code calls critical code SendCommandBitmapSource /// SecurityTreatAsSafe: The code called into demands /// [SecurityCritical, SecurityTreatAsSafe] internal virtual void UpdateBitmapSourceResource(DUCE.Channel channel, bool skipOnChannelCheck) { if (_needsUpdate) { _convertedDUCEPtr = null; _needsUpdate = false; } // If we're told we can skip the channel check, then we must be on channel Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); if (skipOnChannelCheck || _duceResource.IsOnChannel(channel)) { // We may end up loading in the bitmap bits so it's necessary to take the [....] lock here. lock (_syncObject) { channel.SendCommandBitmapSource( _duceResource.GetHandle(channel), DUCECompatiblePtr ); } } } ////// Called when a failure to decode is detected. /// ////// Critical - access critical resource WicSourceHandle /// TreatAsSafe - All inputs verified /// [SecurityCritical, SecurityTreatAsSafe] internal void RecoverFromDecodeFailure(Exception e) { // Set the source to an empty image in case the user doesn't respond to the failed event byte[] pixels = new byte[4]; WicSourceHandle = Create(1, 1, 96, 96, PixelFormats.Pbgra32, null, pixels, 4).WicSourceHandle; IsSourceCached = true; // Let the user know that we've failed to decode so they can gracefully handle the failure. // Typically, the user would replace this image with a "failure" image OnDecodeFailed(this, new ExceptionEventArgs(e)); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); _duceResource.ReleaseOnChannel(channel); } void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { // using (CompositionEngineLock.Acquire()) { ReleaseOnChannelCore(channel); } } internal override DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel) { return _duceResource.GetHandle(channel); } DUCE.ResourceHandle DUCE.IResource.GetHandle(DUCE.Channel channel) { using (CompositionEngineLock.Acquire()) { return GetHandleCore(channel); } } /// Returns the closest format that is supported by the rendering engine internal static PixelFormat GetClosestDUCEFormat(PixelFormat format, BitmapPalette palette) { int i = Array.IndexOf(s_supportedDUCEFormats, format); if (i != -1) { return s_supportedDUCEFormats[i]; } int bitsPerPixel = format.InternalBitsPerPixel; if (bitsPerPixel == 1) { return PixelFormats.Indexed1; } else if (bitsPerPixel == 2) { return PixelFormats.Indexed2; } else if (bitsPerPixel <= 4) { return PixelFormats.Indexed4; } else if (bitsPerPixel <= 8) { return PixelFormats.Indexed8; } else if (bitsPerPixel <= 16 && format.Format != PixelFormatEnum.Gray16) // For Gray16, one of the RGB Formats is closest { return PixelFormats.Bgr555; } else if (format.HasAlpha || BitmapPalette.DoesPaletteHaveAlpha(palette)) { return PixelFormats.Pbgra32; } else { return PixelFormats.Bgr32; } } /// Creates a IWICBitmap ////// Critical - access critical resource /// [SecurityCritical] internal static BitmapSourceSafeMILHandle CreateCachedBitmap( BitmapFrame frame, BitmapSourceSafeMILHandle wicSource, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, BitmapPalette palette ) { BitmapSourceSafeMILHandle wicConverter = null; BitmapSourceSafeMILHandle wicConvertedSource = null; // For NoCache, return the original if (cacheOption == BitmapCacheOption.None) { return wicSource; } using (FactoryMaker factoryMaker = new FactoryMaker()) { IntPtr wicFactory = factoryMaker.ImagingFactoryPtr; bool changeFormat = false; PixelFormat originalFmt = PixelFormats.Pbgra32; WICBitmapCreateCacheOptions wicCache = WICBitmapCreateCacheOptions.WICBitmapCacheOnLoad; if (cacheOption == BitmapCacheOption.OnDemand) { wicCache = WICBitmapCreateCacheOptions.WICBitmapCacheOnDemand; } originalFmt = PixelFormat.GetPixelFormat(wicSource); PixelFormat destFmt = originalFmt; // check that we need to change the format of the bitmap if (0 == (createOptions & BitmapCreateOptions.PreservePixelFormat)) { if (Array.IndexOf(BitmapSource.s_supportedDUCEFormats, originalFmt) == -1) changeFormat = true; destFmt = BitmapSource.GetClosestDUCEFormat(originalFmt, palette); } if (frame != null && (createOptions & BitmapCreateOptions.IgnoreColorProfile) == 0 && frame.ColorContexts != null && frame.ColorContexts[0] != null && frame.ColorContexts[0].IsValid && !frame._isColorCorrected && PixelFormat.GetPixelFormat(wicSource).Format != PixelFormatEnum.Extended ) { ColorContext destinationColorContext; // We need to make sure, we can actually create the ColorContext for the destination destFmt // If the destFmt is gray or scRGB, the following is not supported, so we cannot // create the ColorConvertedBitmap try { destinationColorContext = new ColorContext(destFmt); } catch (NotSupportedException) { destinationColorContext = null; } if (destinationColorContext != null) { // NOTE: Never do this for a non-MIL pixel format, because the format converter has // special knowledge to deal with the profile 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( frame, frame.ColorContexts[0], destinationColorContext, destFmt ); wicSource = colorConvertedBitmap.WicSourceHandle; frame._isColorCorrected = true; conversionSuccess = true; changeFormat = false; // Changeformat no longer necessary, because destFmt already created // by ColorConvertedBitmap } 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 && changeFormat && !badColorContext) { // If the conversion failed, we first use // a FormatConvertedBitmap, and then Color Convert that one... changeFormat = false; FormatConvertedBitmap formatConvertedBitmap = new FormatConvertedBitmap(frame, destFmt, null, 0.0); ColorConvertedBitmap colorConvertedBitmap = new ColorConvertedBitmap( formatConvertedBitmap, frame.ColorContexts[0], destinationColorContext, destFmt ); wicSource = colorConvertedBitmap.WicSourceHandle; frame._isColorCorrected = true; Debug.Assert(destFmt == colorConvertedBitmap.Format); changeFormat = false; // Changeformat no longer necessary, because destFmt already created // by ColorConvertedBitmap } } } if (changeFormat) { // start up a format converter Guid fmtDestFmt = destFmt.Guid; HRESULT.Check(UnsafeNativeMethods.WICCodec.WICConvertBitmapSource( ref fmtDestFmt, wicSource, out wicConverter)); // dump the converted contents into a bitmap HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( wicFactory, wicConverter, wicCache, out wicConvertedSource)); } else { // Create the unmanaged resources HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( wicFactory, wicSource, wicCache, out wicConvertedSource)); } wicConvertedSource.CalculateSize(); } return wicConvertedSource; } /// Called when decode fails private void OnDecodeFailed(object sender, ExceptionEventArgs e) { _decodeFailedEvent.InvokeEvents(this, e); } #region Event handlers for bitmap chains // When the final link in a bitmap chain's download completes, its sets a new WicSourceHandle // This change must propagate up the entire chain through DownloadCompleted events private void OnSourceDownloadCompleted(object sender, EventArgs e) { // _weakBitmapSourceEventSink might be null. If the link down the chain was cloned, then // this link's event listeners would be cloned as well. As a result, this BitmapSource // will be listening to both the original and the clone's events, so it may get the event // twice. // TransformedBitmap --> CachedBitmap // | // | (caused by cloning the event helper on CachedBitmap) // +-------> CachedBitmap (clone) // So this BitmapSource will get DownloadCompleted from both the link down the chain and // its clone at the same time. The first event should be handled normally, whilethe // second event is a duplicate and should be silently ignored. if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); // Need to call FinalizeCreation to create the new WicSourceHandle, but only in some // circumstances. If this BitmapSource isn't done initializing, then there's no need // to call FinalizeCreation since it will be called in EndInit. // FinalizeCreation makes use of properties on the object, and can throw if they're // not set properly. Use IsValidForFinalizeCreation to validate, but don't throw // if the validation fails. if (_bitmapInit.IsInitAtLeastOnce && IsValidForFinalizeCreation(/* throwIfInvalid = */ false)) { // FinalizeCreation() can throw because it usually makes pinvokes to things // that return HRESULTs. Since firing the download events up the chain is // new behavior in 4.0, these exceptions are breaking plus they aren't catchable // by user code. This is mostly here for ColorConvertedBitmap throwing with // a bad color context, but the fact that any BitmapSource could throw and // it wouldn't be catchable is justification enough to eat them all for now... // // See Dev10 #770180 try { FinalizeCreation(); _needsUpdate = true; } catch { } _downloadEvent.InvokeEvents(this, e); } } } private void OnSourceDownloadFailed(object sender, ExceptionEventArgs e) { // _weakBitmapSourceEventSink might be null. If the link down the chain was cloned, then // this link's event listeners would be cloned as well. As a result, this BitmapSource // will be listening to both the original and the clone's events, so it may get the event // twice. // TransformedBitmap --> CachedBitmap // | // | (caused by cloning the event helper on CachedBitmap) // +-------> CachedBitmap (clone) // So this BitmapSource will get DownloadFailed from both the link down the chain and // its clone at the same time. The first event should be handled normally, whilethe // second event is a duplicate and should be silently ignored. if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); _failedEvent.InvokeEvents(this, e); } } private void OnSourceDownloadProgress(object sender, DownloadProgressEventArgs e) { _progressEvent.InvokeEvents(this, e); } private void CleanUpWeakEventSink() { // Situation: // +------+ --a--> +-------------------+ --c--> +------------------+ // | this | | (weak event sink) | | nextBitmapSource | // +------+ <--b-- +-------------------+ <--d-- +------------------+ // | ^ // +---------------------e----------------------------+ // a is the _weakBitmapSourceEventSink reference // b is a WeakReference from _weakBitmapSourceEventSink back to this // c is the WeakBitmapSourceEventSink.EventSource property // d is the implicit reference due to _weakBitmapSourceEventSink attaching a handler // to nextBitmapSource // e is the Source property on TransformedBitmap, etc // nextBitmapSource fired DownloadCompleted/Failed, so we're detaching the event // handlers and cleaning up _weakBitmapSourceEventSink // Remove link c, link d // Remove the reference from the weak event sink // This implicitly removes link d as well (the EventSource setter detaches from the // old event source) // Note: this is NOT redundant, even if you detach explicitly somewhere else // If nextBitmapSource ever gets cloned, then the cloned nextBitmapSource will // contain the same delegates as nextBitmapSource, which includes the weak // event sink. So although we'll be cleaning up link a, the cloned // nextBitmapSource will still have a strong reference back to the weak // event sink _weakBitmapSourceEventSink.EventSource = null; // Remove link a _weakBitmapSourceEventSink = null; // Link b is a WeakReference and will not cause leaks // After: // +------+ +-------------------+ +------------------+ // | this | | (weak event sink) | | nextBitmapSource | // +------+ <--b-- +-------------------+ +------------------+ // | ^ // +---------------------e----------------------------+ // The weak event sink can be collected, unless a cloned nextBitmapSource is // still referencing it. // this can be collected if it's not being explicitly referenced // nextBitmapSource can be collected if it/this isn't being explicitly referenced } // Needs to be internal, called by subclasses to attach handlers when their Source changes internal void RegisterDownloadEventSource(BitmapSource eventSource) { if (_weakBitmapSourceEventSink == null) { _weakBitmapSourceEventSink = new WeakBitmapSourceEventSink(this); } _weakBitmapSourceEventSink.EventSource = eventSource; } // Needs to be internal, called by BitmapImage to detach handlers from the dummy downloading // BitmapFrameDecode's chain internal void UnregisterDownloadEventSource() { if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); } } // Sometimes FinalizeCreation can't be called (e.g. when a ColorConvertedBitmap has no // SourceColorContext or no DestinationColorContext). EndInit needs to make these // checks before it calls FinalizeCreation, but so does OnSourceDownloadCompleted. // Therefore, the validation has been factored out into this method. // If the validation fails in EndInit, an exception should be thrown. If the validation // fails in OnSourceDownloadCompleted, there should be no exception. Therefore, the // throwIfInvalid flag is provided. internal virtual bool IsValidForFinalizeCreation(bool throwIfInvalid) { return true; } #endregion // // Workaround for a behavior change caused by a bug fix // // According to the old implementation, CopyCommon will clone the download events as // well (_asdfEvent = sourceBitmap._asdfEvent.Clone();). The problem with this is the // _asdfEvent.Clone() will also clone all the delegates currently attached to _asdfEvent, // so anyone listening to the original would be implicitly listening to the clone as well. // // The bug was that BitmapFrameDecode's clone was broken if it was made while the original // BFD was still downloading. The clone never does anything, so it doesn't update to show // the image after download completes and doesn't fire any events. So the net effect is // that anyone attached to the original BFD would still see only 1 event get fired despite // implicitly listening to the clone as well. // // The problem comes in when the BFD clone got fixed. It'll now update to show the image, // but it will also start firing events. So people listening to the original BFD will now // see a behavior change (used to get download only once, but will now get download from // the fixed clone as well). This flag is introduced as a workaround. When it's true, // CopyCommon works as it did before: delegates get copied. When it's false, delegates do // not get copied. BitmapFrameDecode will override this to return false in order to // simulate the old BFD behavior when clones didn't fire and the listeners on the original // only got the event once. // internal virtual bool ShouldCloneEventDelegates { get { return true; } } #endregion #region Animatable and Freezable ////// Called by the base Freezable class to make this object /// frozen. /// protected override bool FreezeCore(bool isChecking) { if (!base.FreezeCore(isChecking)) { return false; } if (IsDownloading) { return false; } return true; } ////// Copy the fields not covered by DPs. This is used by /// CloneCore(), CloneCurrentValueCore(), GetAsFrozenCore() and /// GetCurrentValueAsFrozenCore(). /// ////// SecurityCritical: This code accesses unmanaged resources /// SecurityTreatAsSafe: Inputs are verified /// [SecurityCritical, SecurityTreatAsSafe] private void CopyCommon(BitmapSource sourceBitmap) { _useVirtuals = sourceBitmap._useVirtuals; _delayCreation = sourceBitmap.DelayCreation; _creationComplete = sourceBitmap.CreationCompleted; WicSourceHandle = sourceBitmap.WicSourceHandle; // always do this near the top _syncObject = sourceBitmap.SyncObject; IsSourceCached = sourceBitmap.IsSourceCached; // // Decide on whether to stick with the old behavior of cloning delegates attached to // the events or not depending on the ShouldCloneEventDelegates property. It always // returns true, except in BitmapFrameDecode where it's overridden to return false. // See the comments for ShouldCloneEventDelegates for more details. // if (ShouldCloneEventDelegates) { if (sourceBitmap._downloadEvent != null) { _downloadEvent = sourceBitmap._downloadEvent.Clone(); } if (sourceBitmap._progressEvent != null) { _progressEvent = sourceBitmap._progressEvent.Clone(); } if (sourceBitmap._failedEvent != null) { _failedEvent = sourceBitmap._failedEvent.Clone(); } if (sourceBitmap._decodeFailedEvent != null) { _decodeFailedEvent = sourceBitmap._decodeFailedEvent.Clone(); } } // // else do nothing // the events are already created (they're initialized in the field declarations) // _format = sourceBitmap.Format; _pixelWidth = sourceBitmap.PixelWidth; _pixelHeight = sourceBitmap.PixelHeight; _dpiX = sourceBitmap.DpiX; _dpiY = sourceBitmap.DpiY; _palette = sourceBitmap.Palette; // // If a BitmapSource is part of a chain, then it will have handlers attached // to the BitmapSource down the chain, X. When X gets cloned, the delegates // in its events get cloned as well, so now the original BitmapSource will have // handlers attached both to X and the X's clone. Here, have the BitmapSource // detach from X's clone. // Note that since DependencyProperties are cloned first and since Source is a // DependencyProperty, the clone's Source would already be set at this point, // and the SourcePropertyChangedHook would have created a _weakBitmapSourceEventSink // object. // if (_weakBitmapSourceEventSink != null && sourceBitmap._weakBitmapSourceEventSink != null) { sourceBitmap._weakBitmapSourceEventSink.DetachSourceDownloadHandlers( _weakBitmapSourceEventSink.EventSource); } } ////// Implementation of protected override void CloneCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.CloneCore . ////// Implementation of protected override void CloneCurrentValueCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.CloneCurrentValueCore . ////// Implementation of protected override void GetAsFrozenCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.GetAsFrozenCore . ////// Implementation of protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } #endregion #region Data Members private bool _delayCreation = false; private bool _creationComplete = false; private bool _useVirtuals = false; internal BitmapInitialize _bitmapInit = new BitmapInitialize(); ///Freezable.GetCurrentValueAsFrozenCore . ////// Critical: Unmanaged IWICBitmapSource handle. Access only through WicSourceHandle /// [SecurityCritical] internal BitmapSourceSafeMILHandle _wicSource = null; ////// Critical: Unmanaged IWICBitmapSource handle. /// [SecurityCritical] internal BitmapSourceSafeMILHandle _convertedDUCEPtr; internal object _syncObject; internal bool _isSourceCached; // Setting this to true causes us to throw away the old DUCECompatiblePtr which contains // a cache of the bitmap in video memory. We'll create a new DUCECompatiblePtr and // eventually send the bitmap to video memory again. internal bool _needsUpdate; internal bool _isColorCorrected; internal UniqueEventHelper _downloadEvent = new UniqueEventHelper(); internal UniqueEventHelper_progressEvent = new UniqueEventHelper (); internal UniqueEventHelper _failedEvent = new UniqueEventHelper (); internal UniqueEventHelper _decodeFailedEvent = new UniqueEventHelper (); // cached properties. should always reflect the unmanaged copy internal PixelFormat _format = PixelFormats.Default; internal int _pixelWidth = 0; internal int _pixelHeight = 0; internal double _dpiX = 96.0; internal double _dpiY = 96.0; internal BitmapPalette _palette = null; /// Duce resource internal DUCE.MultiChannelResource _duceResource = new DUCE.MultiChannelResource(); /// List of supported DUCE formats /// NOTE: Please add formats in increasing bpp order private readonly static PixelFormat[] s_supportedDUCEFormats = new PixelFormat[13] { PixelFormats.Indexed1, PixelFormats.BlackWhite, PixelFormats.Indexed2, PixelFormats.Gray2, PixelFormats.Indexed4, PixelFormats.Gray4, PixelFormats.Indexed8, PixelFormats.Gray8, PixelFormats.Bgr555, PixelFormats.Bgr565, PixelFormats.Bgr32, PixelFormats.Bgra32, PixelFormats.Pbgra32 }; // For propagating events in bitmap chains private WeakBitmapSourceEventSink _weakBitmapSourceEventSink = null; #endregion #region Interface definitions //************************************************************* // // IWICBitmapSource // //************************************************************* // Guid: IID_IWICBitmapSource [ComImport(), Guid("00000120-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] internal interface IWICBitmapSource { [PreserveSig] int GetSize( out int puiWidth, out int puiHeight ); [PreserveSig] int GetPixelFormat( out Guid guidFormat ); [PreserveSig] int GetResolution( out double pDpiX, out double pDpiY ); [PreserveSig] int GetPalette( IntPtr /* IWICPalette */ pIPalette ); // SizeParamIndex says which parameter specifies the size of the array, // which we are saying is cbStride. [PreserveSig] int CopyPixels( IntPtr prc, int cbStride, int cbPixels, IntPtr pvPixels ); } #endregion #region Managed Bitmap Source //************************************************************** // // ManagedBitmapSource // //************************************************************* [ClassInterface(ClassInterfaceType.None)] internal class ManagedBitmapSource : IWICBitmapSource { private BitmapSource _bitmapSource; private ManagedBitmapSource() { } public ManagedBitmapSource(BitmapSource bitmapSource) { if (bitmapSource == null) { throw new System.ArgumentNullException("bitmapSource"); } _bitmapSource = bitmapSource; } int IWICBitmapSource.GetSize(out int puiWidth, out int puiHeight) { puiWidth = _bitmapSource.PixelWidth; puiHeight = _bitmapSource.PixelHeight; return NativeMethods.S_OK; } int IWICBitmapSource.GetPixelFormat(out Guid guidFormat) { guidFormat = _bitmapSource.Format.Guid; return NativeMethods.S_OK; } int IWICBitmapSource.GetResolution(out double pDpiX, out double pDpiY) { pDpiX = _bitmapSource.DpiX; pDpiY = _bitmapSource.DpiY; return NativeMethods.S_OK; } /// /// Critical - Calls into a P/Invoke method InitializeFromPalette(). /// [SecurityCritical] int IWICBitmapSource.GetPalette(IntPtr /* IWICPalette */ pIPalette) { BitmapPalette palette = _bitmapSource.Palette; if ((palette == null) || (palette.InternalPalette == null) || (SecurityHelper.SafeHandleIsInvalid(palette.InternalPalette))) { return (int)WinCodecErrors.WINCODEC_ERR_PALETTEUNAVAILABLE; } HRESULT.Check(UnsafeNativeMethods.WICPalette.InitializeFromPalette(pIPalette, palette.InternalPalette)); return NativeMethods.S_OK; } ////// Critical: This code can be used to expose pixel information /// [SecurityCritical] int IWICBitmapSource.CopyPixels(IntPtr prc, int cbStride, int cbPixels, IntPtr pvPixels) { if (cbStride < 0) { return NativeMethods.E_INVALIDARG; } if (pvPixels == IntPtr.Zero) { return NativeMethods.E_INVALIDARG; } Int32Rect rc; if (prc == IntPtr.Zero) { rc = new Int32Rect(0, 0, _bitmapSource.PixelWidth, _bitmapSource.PixelHeight); } else { rc = (Int32Rect)Marshal.PtrToStructure(prc, typeof(Int32Rect)); } int rectHeight, rectWidth; rectHeight = rc.Height; rectWidth = rc.Width; if (rc.Width < 1 || rc.Height < 1) { return NativeMethods.E_INVALIDARG; } // assuming cbStride can't be negative, but that prc.Height can PixelFormat pfStruct = _bitmapSource.Format; if (pfStruct.Format == PixelFormatEnum.Default || pfStruct.Format == PixelFormatEnum.Extended) { return (int)(WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT); } int rectRowSize = checked((rectWidth * pfStruct.InternalBitsPerPixel + 7) / 8); if (cbPixels < checked((rectHeight - 1) * cbStride + rectRowSize)) { return (int)(WinCodecErrors.WINCODEC_ERR_INSUFFICIENTBUFFER); } // Need to marshal int arraySize = checked(rectHeight * rectRowSize); byte[] managedArray = new byte[arraySize]; // perform the copy _bitmapSource.CopyPixels(rc, managedArray, rectRowSize, 0); { // transfer the contents of the relevant rect from the managed array to pvPixels long rowPtr = pvPixels.ToInt64(); for (int y = 0; y < rectHeight; y++) { Marshal.Copy(managedArray, y * rectRowSize, new IntPtr(rowPtr), rectRowSize); rowPtr += cbStride; } } return NativeMethods.S_OK; } } #endregion #region WeakBitmapSourceEventSink // Used to propagate DownloadCompleted events for bitmap chains // The simple way is to just have this BitmapSource add handlers to the chained // BitmapSource's DownloadCompleted/DownloadFailed event. But that creates // an implicit strong reference from the child back to this. Since multiple chains can // contain the child, this BitmapSource can prevent BitmapSources in another chain // from being collected. // (A) <-----------------> (B) // (C) <--> (D) <--> (E) <--^ // A existing prevents C's entire chain from being collected // The weak event sink prevents a strong reference from the chained BitmapSource // back to this one. // (A) --> (weak) <--> (B) private class WeakBitmapSourceEventSink : WeakReference { public WeakBitmapSourceEventSink(BitmapSource bitmapSource) : base(bitmapSource) { } public void OnSourceDownloadCompleted(object sender, EventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadCompleted(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void OnSourceDownloadFailed(object sender, ExceptionEventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadFailed(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void OnSourceDownloadProgress(object sender, DownloadProgressEventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadProgress(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void DetachSourceDownloadHandlers(BitmapSource source) { if (!source.IsFrozen) { source.DownloadCompleted -= OnSourceDownloadCompleted; source.DownloadFailed -= OnSourceDownloadFailed; source.DownloadProgress -= OnSourceDownloadProgress; } } public void AttachSourceDownloadHandlers() { if (!_eventSource.IsFrozen) { _eventSource.DownloadCompleted += OnSourceDownloadCompleted; _eventSource.DownloadFailed += OnSourceDownloadFailed; _eventSource.DownloadProgress += OnSourceDownloadProgress; } } private BitmapSource _eventSource; public BitmapSource EventSource { get { return _eventSource; } set { if (_eventSource != null) { DetachSourceDownloadHandlers(_eventSource); } _eventSource = value; if (_eventSource != null) { AttachSourceDownloadHandlers(); } } } } #endregion } #endregion // BitmapSource } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Microsoft Avalon // Copyright (c) Microsoft Corporation, All Rights Reserved. // // File: BitmapSource.cs // //----------------------------------------------------------------------------- using System; using System.IO; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Security; using System.Net; using System.Security.Permissions; using System.Runtime.InteropServices; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using MS.Win32; using System.IO.Packaging; using UnsafeNativeMethods = MS.Win32.PresentationCore.UnsafeNativeMethods; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; using MS.Internal.PresentationCore; // SecurityHelper namespace System.Windows.Media.Imaging { #region BitmapSource ////// Interface for Bitmap Sources, included decoders and effects /// [Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)] public abstract class BitmapSource : ImageSource, DUCE.IResource { #region Constructor ////// Create a BitmapSource from an array of pixels. /// /// Width of the Bitmap /// Height of the Bitmap /// Horizontal DPI of the Bitmap /// Vertical DPI of the Bitmap /// Format of the Bitmap /// Palette of the Bitmap /// Array of pixels /// stride public static BitmapSource Create( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, Imaging.BitmapPalette palette, System.Array pixels, int stride ) { return new CachedBitmap( pixelWidth, pixelHeight, dpiX, dpiY, pixelFormat, palette, pixels, stride); } ////// Create a BitmapSource from an array of pixels in unmanaged memory. /// /// Width of the Bitmap /// Height of the Bitmap /// Horizontal DPI of the Bitmap /// Vertical DPI of the Bitmap /// Format of the Bitmap /// Palette of the Bitmap /// Pointer to the buffer in memory /// Size of the buffer /// stride ////// Callers must have UnmanagedCode permission to call this API. /// ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] unsafe public static BitmapSource Create( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, Imaging.BitmapPalette palette, IntPtr buffer, int bufferSize, int stride ) { SecurityHelper.DemandUnmanagedCode(); return new CachedBitmap( pixelWidth, pixelHeight, dpiX, dpiY, pixelFormat, palette, buffer, bufferSize, stride); } ////// Constructor /// ////// Critical: Accesses _wicSource /// TreatAsSafe: Inputs are safe /// [SecurityCritical, SecurityTreatAsSafe] protected BitmapSource() { // Synchronize for *this* object only by default. _syncObject = _bitmapInit; _isSourceCached = false; } ////// Internal Constructor /// /// useVirtuals: Should properties and methods like PixelWidth and CopyPixels use their "default" implementation. /// ////// Critical: Accesses _wicSource /// TreatAsSafe: Input (bool) is safe /// [SecurityCritical, SecurityTreatAsSafe] internal BitmapSource(bool useVirtuals) { _useVirtuals = true; _isSourceCached = false; // Synchronize for *this* object only by default. _syncObject = _bitmapInit; } ////// Creates a copy of this object. /// ///The copy. public new BitmapSource Clone() { return (BitmapSource)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed version for convenience. /// ///The copy. public new BitmapSource CloneCurrentValue() { return (BitmapSource)base.CloneCurrentValue(); } #endregion Constructor #region Public properties and methods ////// Native format of the bitmap's data. /// If the BitmapSource is directly readable, this is the format the /// pixels will be in when they are read. /// public virtual System.Windows.Media.PixelFormat Format { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _format; } } ////// Width, in pixels, of the bitmap. /// public virtual int PixelWidth { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _pixelWidth; } } ////// Height, in pixels, of the bitmap. /// public virtual int PixelHeight { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _pixelHeight; } } ////// Horizontal DPI of the bitmap. /// public virtual double DpiX { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _dpiX; } } ////// Vertical DPI of the bitmap. /// public virtual double DpiY { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); return _dpiY; } } ////// Retrieve and set the bitmap palette. /// public virtual Imaging.BitmapPalette Palette { get { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); if (_palette == null) { // update the local palette if (_format.Palettized) { _palette = Imaging.BitmapPalette.CreateFromBitmapSource(this); } } return _palette; } } ////// Returns true if the BitmapSource is downloading content /// public virtual bool IsDownloading { get { ReadPreamble(); return false; } } ////// Raised when downloading content is done /// May not be raised for all content. /// public virtual event EventHandler DownloadCompleted { add { WritePreamble(); _downloadEvent.AddEvent(value); } remove { WritePreamble(); _downloadEvent.RemoveEvent(value); } } ////// Raised when download has progressed /// May not be raised for all content. /// public virtual event EventHandlerDownloadProgress { add { WritePreamble(); _progressEvent.AddEvent(value); } remove { WritePreamble(); _progressEvent.RemoveEvent(value); } } /// /// Raised when download has failed /// May not be raised for all content. /// public virtual event EventHandlerDownloadFailed { add { WritePreamble(); _failedEvent.AddEvent(value); } remove { WritePreamble(); _failedEvent.RemoveEvent(value); } } /// /// Raised when decoding has failed /// May not be raised for all content. /// public virtual event EventHandlerDecodeFailed { add { WritePreamble(); EnsureShouldUseVirtuals(); _decodeFailedEvent.AddEvent(value); } remove { WritePreamble(); EnsureShouldUseVirtuals(); _decodeFailedEvent.RemoveEvent(value); } } /// /// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). An empty rect (all 0s) will copy the /// entire bitmap. /// /// Source rect to copy. Int32Rect.Empty specifies the entire rect /// Destination array /// Stride /// Offset in the array to begin copying ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { EnsureShouldUseVirtuals(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CriticalCopyPixels(sourceRect, pixels, stride, offset); } ////// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). /// /// Destination array /// Stride /// Offset to begin at ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Array pixels, int stride, int offset) { Int32Rect sourceRect = Int32Rect.Empty; EnsureShouldUseVirtuals(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CopyPixels(sourceRect, pixels, stride, offset); } ////// Copy the pixel data from the bitmap into the array of pixels that /// has the specified stride, starting at the offset (specified in number /// of pixels from the beginning). An empty rect (all 0s) will copy the /// entire bitmap. /// /// Source rect to copy. Int32Rect.Empty specified entire Bitmap /// Pointer to the buffer /// Size of buffer /// Stride ////// Critical: This code can be used to expose pixel information /// PublicOk: There is an inheritance demand to prevent sub classing and there is /// a web permission demand to prevent non site of origin requests /// [SecurityCritical] [SecurityPermission(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)] public virtual void CopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride) { ReadPreamble(); EnsureShouldUseVirtuals(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); // Demand Site Of origin on the URI if it passes then this information is ok to expose CheckIfSiteOfOrigin(); CriticalCopyPixels(sourceRect, buffer, bufferSize, stride); } ////// Get the width of the bitmap in measure units (96ths of an inch). /// public override double Width { get { ReadPreamble(); return GetWidthInternal(); } } ////// Get the width of the bitmap in measure units (96ths of an inch). /// public override double Height { get { ReadPreamble(); return GetHeightInternal(); } } ////// Get the Metadata of the bitmap /// public override ImageMetadata Metadata { get { ReadPreamble(); return null; } } #endregion #region Internal, Protected and Private properties and methods ////// Helper function to calculate Width. /// private double GetWidthInternal() { return ImageSource.PixelsToDIPs(this.DpiX, this.PixelWidth); } ////// Helper function to calculate Height. /// private double GetHeightInternal() { return ImageSource.PixelsToDIPs(this.DpiY, this.PixelHeight); } ////// Get the Size for the bitmap /// internal override Size Size { get { ReadPreamble(); return new Size(Math.Max(0, GetWidthInternal()), Math.Max(0, GetHeightInternal())); } } internal bool DelayCreation { get { return _delayCreation; } set { _delayCreation = value; if (_delayCreation) { CreationCompleted = false; } } } internal bool CreationCompleted { get { return _creationComplete; } set { _creationComplete = value; } } /// /// Demand that the bitmap should be created if it was delay-created. /// internal void CompleteDelayedCreation() { // Protect against multithreaded contention on delayed creation. if (DelayCreation) { lock (_syncObject) { if (DelayCreation) { EnsureShouldUseVirtuals(); DelayCreation = false; try { FinalizeCreation(); } catch { DelayCreation = true; throw; } CreationCompleted = true; } } } } internal virtual void FinalizeCreation() { throw new NotImplementedException(); } private void EnsureShouldUseVirtuals() { if (_useVirtuals == false) { throw new NotImplementedException(); } } internal object SyncObject { get { Debug.Assert(_syncObject != null); return _syncObject; } } internal bool IsSourceCached { get { return _isSourceCached; } set { _isSourceCached = value; } } ////// Critical - access safehandle that points to unmanaged resource /// TreatAsSafe - Set: Its safe because any value is OK as the SafeHandle cannot be created /// by an arbitrary IntPtr. /// internal BitmapSourceSafeMILHandle WicSourceHandle { [SecurityCritical] get { CompleteDelayedCreation(); if (_wicSource == null || _wicSource.IsInvalid) { ManagedBitmapSource managedBitmapSource = new ManagedBitmapSource(this); _wicSource = new BitmapSourceSafeMILHandle(Marshal.GetComInterfaceForObject( managedBitmapSource, typeof(System.Windows.Media.Imaging.BitmapSource.IWICBitmapSource))); } return _wicSource; } [SecurityCritical, SecurityTreatAsSafe] set { if (value != null) { IntPtr wicSource = IntPtr.Zero; Guid _uuidWicBitmapSource = MILGuidData.IID_IWICBitmapSource; HRESULT.Check(UnsafeNativeMethods.MILUnknown.QueryInterface( value, ref _uuidWicBitmapSource, out wicSource)); _wicSource = new BitmapSourceSafeMILHandle(wicSource, value); UpdateCachedSettings(); } else { _wicSource = null; } } } /// /// Update local variables from the unmanaged resource /// ////// Critical - access/creates critical resources /// [SecurityCritical] internal virtual void UpdateCachedSettings() { EnsureShouldUseVirtuals(); uint pw, ph; lock (_syncObject) { _format = PixelFormat.GetPixelFormat(_wicSource); HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.GetSize( _wicSource, out pw, out ph)); HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.GetResolution( _wicSource, out _dpiX, out _dpiY)); } _pixelWidth = (int)pw; _pixelHeight = (int)ph; } ////// CriticalCopyPixels /// /// /// /// /// ////// Critical: This code can be used to expose pixel information /// [SecurityCritical] [FriendAccessAllowed] // Built into Core, also used by Framework. unsafe internal void CriticalCopyPixels(Int32Rect sourceRect, Array pixels, int stride, int offset) { ReadPreamble(); _bitmapInit.EnsureInitializedComplete(); CompleteDelayedCreation(); if (pixels == null) throw new System.ArgumentNullException("pixels"); if (pixels.Rank != 1) throw new ArgumentException(SR.Get(SRID.Collection_BadRank), "pixels"); if (offset < 0) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } int elementSize = -1; if (pixels is byte[]) elementSize = 1; else if (pixels is short[] || pixels is ushort[]) elementSize = 2; else if (pixels is int[] || pixels is uint[] || pixels is float[]) elementSize = 4; else if (pixels is double[]) elementSize = 8; if (elementSize == -1) throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); int destBufferSize = checked(elementSize * (pixels.Length - offset)); if (pixels is byte[]) { fixed (void* pixelArray = &((byte[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is short[]) { fixed (void* pixelArray = &((short[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is ushort[]) { fixed (void* pixelArray = &((ushort[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is int[]) { fixed (void* pixelArray = &((int[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is uint[]) { fixed (void* pixelArray = &((uint[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is float[]) { fixed (void* pixelArray = &((float[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } else if (pixels is double[]) { fixed (void* pixelArray = &((double[])pixels)[offset]) CriticalCopyPixels(sourceRect, (IntPtr)pixelArray, destBufferSize, stride); } } ////// CriticalCopyPixels /// /// /// /// /// ////// Critical: There is a risk of buffer overruns when copying data to the buffer. /// This code can also be used to expose pixel information /// [SecurityCritical] internal void CriticalCopyPixels(Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride) { if (buffer == IntPtr.Zero) throw new ArgumentNullException("buffer"); if (stride <= 0) throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterMustBeGreaterThanZero)); if (sourceRect.Width <= 0) sourceRect.Width = PixelWidth; if (sourceRect.Height <= 0) sourceRect.Height = PixelHeight; if (sourceRect.Width > PixelWidth) throw new ArgumentOutOfRangeException("sourceRect.Width", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelWidth)); if (sourceRect.Height > PixelHeight) throw new ArgumentOutOfRangeException("sourceRect.Height", SR.Get(SRID.ParameterCannotBeGreaterThan, PixelHeight)); int minStride = checked(((sourceRect.Width * Format.BitsPerPixel) + 7) / 8); if (stride < minStride) throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, minStride)); int minRequiredDestSize = checked((stride * (sourceRect.Height - 1)) + minStride); if (bufferSize < minRequiredDestSize) throw new ArgumentOutOfRangeException("buffer", SR.Get(SRID.ParameterCannotBeLessThan, minRequiredDestSize)); lock (_syncObject) { HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.CopyPixels( WicSourceHandle, ref sourceRect, (uint)stride, (uint)bufferSize, buffer )); } } ////// Critical: This code is used to check and grant access to pixel data /// TreatAsSafe: This code does not elevate /// [SecurityCritical, SecurityTreatAsSafe] protected void CheckIfSiteOfOrigin() { string uri = null; // This call is inheritance demand protected. It is overridden in // BitmapFrameDecoder and BitmapImage if (CanSerializeToString()) { // This call returns the URI either as an absolute URI which the user // passed in, in the first place or as the string "image" // we only allow this code to succeed in the case of Uri and if it is site of // origin or pack:. In all other conditions we fail uri = ConvertToString(null, null); } SecurityHelper.DemandMediaAccessPermission(uri); } ////// Called when DUCE resource requires updating /// ////// Critical - access critical resource _convertedDUCEPtr /// TreatAsSafe - All inputs verified /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck) { base.UpdateResource(channel, skipOnChannelCheck); UpdateBitmapSourceResource(channel, skipOnChannelCheck); } ////// Critical - access/create critical resource, returns unmanaged pointer /// internal virtual BitmapSourceSafeMILHandle DUCECompatiblePtr { [SecurityCritical] get { BitmapSourceSafeMILHandle /* IWICBitmapSource */ pIWICSource = WicSourceHandle; BitmapSourceSafeMILHandle /* CWICWrapperBitmap as IWICBitmapSource */ pCWICWrapperBitmap = null; // if we've already cached the ptr, reuse it. if (_convertedDUCEPtr != null && !_convertedDUCEPtr.IsInvalid) { // already in friendly format Debug.Assert(_isSourceCached); } else { bool supportedFormat = (Array.IndexOf(s_supportedDUCEFormats, Format) != -1); if (supportedFormat && _isSourceCached) { #region Make sure the image is decoded on the UI thread // In the case that the source is cached (ie it is already an IWICBitmap), // its possible that the bitmap is a demand bitmap. The demand bitmap only // copies the source bits when absolutely required (ie CopyPixels). This means // that if a decode frame is attached to the demand bitmap, it may decode bits // on the render thread (bad!). To prevent that, we call CopyPixels for the first // pixel which will decode the entire image. // // Ideally, we need an implementation of IWICBitmap that can cache on a scanline // basis as well can be forced to "realize" its cache when requested. Consider // adding a method on IWICBitmap such as RealizeCache(WICRect *). // Int32Rect sourceRect = new Int32Rect(0, 0, 1, 1); PixelFormat format = Format; int bufferSize = (format.BitsPerPixel + 7) / 8; byte[] buffer = new byte[bufferSize]; // If the bitmap has corrupt pixel data, we may not have detected it until now. // At this point the user cannot recover gracefully, so we'll display a 1x1 image // similar to what LateBoundDecoder does before it's done downloading. try { unsafe { fixed (void* pixelArray = &((byte[])buffer)[0]) { HRESULT.Check(UnsafeNativeMethods.WICBitmapSource.CopyPixels( pIWICSource, ref sourceRect, (uint)bufferSize, (uint)bufferSize, (IntPtr)pixelArray )); } } } catch (Exception e) { RecoverFromDecodeFailure(e); // the source will change during recovery, so we need to grab its new value pIWICSource = WicSourceHandle; } #endregion } else // needs caching { BitmapSourceSafeMILHandle pIWicConverter = null; using (FactoryMaker factoryMaker = new FactoryMaker()) { try { if (!supportedFormat) { #region Convert the source to a compatible format that's writable Guid destFmt = GetClosestDUCEFormat(Format, Palette).Guid; // This forces a cached system memory copy of the image in PARGB32 format. This is // necessary to avoid format conversion in the UCE during render and accompanying // sychronization locks with UI thread during bitmap access. HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateFormatConverter( factoryMaker.ImagingFactoryPtr, out pIWicConverter)); HRESULT.Check(UnsafeNativeMethods.WICFormatConverter.Initialize( pIWicConverter, pIWICSource, ref destFmt, DitherType.DitherTypeNone, new SafeMILHandle(IntPtr.Zero), 0, WICPaletteType.WICPaletteTypeCustom)); pIWICSource = pIWicConverter; #endregion } #region Cache the source in memory to ensure it's not decoded/converted on the render thread try { HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( factoryMaker.ImagingFactoryPtr, pIWICSource, WICBitmapCreateCacheOptions.WICBitmapCacheOnLoad, out pIWICSource)); } catch (Exception e) { RecoverFromDecodeFailure(e); // the source will change during recovery, so we need to grab its new value pIWICSource = WicSourceHandle; } _isSourceCached = true; #endregion } finally { if (pIWicConverter != null) pIWicConverter.Close(); } } } HRESULT.Check(UnsafeNativeMethods.MilCoreApi.CreateCWICWrapperBitmap( pIWICSource, out pCWICWrapperBitmap)); UnsafeNativeMethods.MILUnknown.AddRef(pCWICWrapperBitmap); _convertedDUCEPtr = new BitmapSourceSafeMILHandle(pCWICWrapperBitmap.DangerousGetHandle(), pIWICSource); } return _convertedDUCEPtr; } } internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { if (_duceResource.CreateOrAddRefOnChannel(this, channel, DUCE.ResourceType.TYPE_BITMAPSOURCE)) { UpdateResource(channel, true /* skip "on channel" check - we already know that we're on channel */ ); } return _duceResource.GetHandle(channel); } DUCE.ResourceHandle DUCE.IResource.AddRefOnChannel(DUCE.Channel channel) { // Reconsider the need for this lock when removing the MultiChannelResource. using (CompositionEngineLock.Acquire()) { return AddRefOnChannelCore(channel); } } internal override int GetChannelCountCore() { return _duceResource.GetChannelCount(); } int DUCE.IResource.GetChannelCount() { return GetChannelCountCore(); } internal override DUCE.Channel GetChannelCore(int index) { return _duceResource.GetChannel(index); } DUCE.Channel DUCE.IResource.GetChannel(int index) { return GetChannelCore(index); } ////// SecurityCritical: This code calls critical code SendCommandBitmapSource /// SecurityTreatAsSafe: The code called into demands /// [SecurityCritical, SecurityTreatAsSafe] internal virtual void UpdateBitmapSourceResource(DUCE.Channel channel, bool skipOnChannelCheck) { if (_needsUpdate) { _convertedDUCEPtr = null; _needsUpdate = false; } // If we're told we can skip the channel check, then we must be on channel Debug.Assert(!skipOnChannelCheck || _duceResource.IsOnChannel(channel)); if (skipOnChannelCheck || _duceResource.IsOnChannel(channel)) { // We may end up loading in the bitmap bits so it's necessary to take the [....] lock here. lock (_syncObject) { channel.SendCommandBitmapSource( _duceResource.GetHandle(channel), DUCECompatiblePtr ); } } } ////// Called when a failure to decode is detected. /// ////// Critical - access critical resource WicSourceHandle /// TreatAsSafe - All inputs verified /// [SecurityCritical, SecurityTreatAsSafe] internal void RecoverFromDecodeFailure(Exception e) { // Set the source to an empty image in case the user doesn't respond to the failed event byte[] pixels = new byte[4]; WicSourceHandle = Create(1, 1, 96, 96, PixelFormats.Pbgra32, null, pixels, 4).WicSourceHandle; IsSourceCached = true; // Let the user know that we've failed to decode so they can gracefully handle the failure. // Typically, the user would replace this image with a "failure" image OnDecodeFailed(this, new ExceptionEventArgs(e)); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); _duceResource.ReleaseOnChannel(channel); } void DUCE.IResource.ReleaseOnChannel(DUCE.Channel channel) { // using (CompositionEngineLock.Acquire()) { ReleaseOnChannelCore(channel); } } internal override DUCE.ResourceHandle GetHandleCore(DUCE.Channel channel) { return _duceResource.GetHandle(channel); } DUCE.ResourceHandle DUCE.IResource.GetHandle(DUCE.Channel channel) { using (CompositionEngineLock.Acquire()) { return GetHandleCore(channel); } } /// Returns the closest format that is supported by the rendering engine internal static PixelFormat GetClosestDUCEFormat(PixelFormat format, BitmapPalette palette) { int i = Array.IndexOf(s_supportedDUCEFormats, format); if (i != -1) { return s_supportedDUCEFormats[i]; } int bitsPerPixel = format.InternalBitsPerPixel; if (bitsPerPixel == 1) { return PixelFormats.Indexed1; } else if (bitsPerPixel == 2) { return PixelFormats.Indexed2; } else if (bitsPerPixel <= 4) { return PixelFormats.Indexed4; } else if (bitsPerPixel <= 8) { return PixelFormats.Indexed8; } else if (bitsPerPixel <= 16 && format.Format != PixelFormatEnum.Gray16) // For Gray16, one of the RGB Formats is closest { return PixelFormats.Bgr555; } else if (format.HasAlpha || BitmapPalette.DoesPaletteHaveAlpha(palette)) { return PixelFormats.Pbgra32; } else { return PixelFormats.Bgr32; } } /// Creates a IWICBitmap ////// Critical - access critical resource /// [SecurityCritical] internal static BitmapSourceSafeMILHandle CreateCachedBitmap( BitmapFrame frame, BitmapSourceSafeMILHandle wicSource, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, BitmapPalette palette ) { BitmapSourceSafeMILHandle wicConverter = null; BitmapSourceSafeMILHandle wicConvertedSource = null; // For NoCache, return the original if (cacheOption == BitmapCacheOption.None) { return wicSource; } using (FactoryMaker factoryMaker = new FactoryMaker()) { IntPtr wicFactory = factoryMaker.ImagingFactoryPtr; bool changeFormat = false; PixelFormat originalFmt = PixelFormats.Pbgra32; WICBitmapCreateCacheOptions wicCache = WICBitmapCreateCacheOptions.WICBitmapCacheOnLoad; if (cacheOption == BitmapCacheOption.OnDemand) { wicCache = WICBitmapCreateCacheOptions.WICBitmapCacheOnDemand; } originalFmt = PixelFormat.GetPixelFormat(wicSource); PixelFormat destFmt = originalFmt; // check that we need to change the format of the bitmap if (0 == (createOptions & BitmapCreateOptions.PreservePixelFormat)) { if (Array.IndexOf(BitmapSource.s_supportedDUCEFormats, originalFmt) == -1) changeFormat = true; destFmt = BitmapSource.GetClosestDUCEFormat(originalFmt, palette); } if (frame != null && (createOptions & BitmapCreateOptions.IgnoreColorProfile) == 0 && frame.ColorContexts != null && frame.ColorContexts[0] != null && frame.ColorContexts[0].IsValid && !frame._isColorCorrected && PixelFormat.GetPixelFormat(wicSource).Format != PixelFormatEnum.Extended ) { ColorContext destinationColorContext; // We need to make sure, we can actually create the ColorContext for the destination destFmt // If the destFmt is gray or scRGB, the following is not supported, so we cannot // create the ColorConvertedBitmap try { destinationColorContext = new ColorContext(destFmt); } catch (NotSupportedException) { destinationColorContext = null; } if (destinationColorContext != null) { // NOTE: Never do this for a non-MIL pixel format, because the format converter has // special knowledge to deal with the profile 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( frame, frame.ColorContexts[0], destinationColorContext, destFmt ); wicSource = colorConvertedBitmap.WicSourceHandle; frame._isColorCorrected = true; conversionSuccess = true; changeFormat = false; // Changeformat no longer necessary, because destFmt already created // by ColorConvertedBitmap } 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 && changeFormat && !badColorContext) { // If the conversion failed, we first use // a FormatConvertedBitmap, and then Color Convert that one... changeFormat = false; FormatConvertedBitmap formatConvertedBitmap = new FormatConvertedBitmap(frame, destFmt, null, 0.0); ColorConvertedBitmap colorConvertedBitmap = new ColorConvertedBitmap( formatConvertedBitmap, frame.ColorContexts[0], destinationColorContext, destFmt ); wicSource = colorConvertedBitmap.WicSourceHandle; frame._isColorCorrected = true; Debug.Assert(destFmt == colorConvertedBitmap.Format); changeFormat = false; // Changeformat no longer necessary, because destFmt already created // by ColorConvertedBitmap } } } if (changeFormat) { // start up a format converter Guid fmtDestFmt = destFmt.Guid; HRESULT.Check(UnsafeNativeMethods.WICCodec.WICConvertBitmapSource( ref fmtDestFmt, wicSource, out wicConverter)); // dump the converted contents into a bitmap HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( wicFactory, wicConverter, wicCache, out wicConvertedSource)); } else { // Create the unmanaged resources HRESULT.Check(UnsafeNativeMethods.WICImagingFactory.CreateBitmapFromSource( wicFactory, wicSource, wicCache, out wicConvertedSource)); } wicConvertedSource.CalculateSize(); } return wicConvertedSource; } /// Called when decode fails private void OnDecodeFailed(object sender, ExceptionEventArgs e) { _decodeFailedEvent.InvokeEvents(this, e); } #region Event handlers for bitmap chains // When the final link in a bitmap chain's download completes, its sets a new WicSourceHandle // This change must propagate up the entire chain through DownloadCompleted events private void OnSourceDownloadCompleted(object sender, EventArgs e) { // _weakBitmapSourceEventSink might be null. If the link down the chain was cloned, then // this link's event listeners would be cloned as well. As a result, this BitmapSource // will be listening to both the original and the clone's events, so it may get the event // twice. // TransformedBitmap --> CachedBitmap // | // | (caused by cloning the event helper on CachedBitmap) // +-------> CachedBitmap (clone) // So this BitmapSource will get DownloadCompleted from both the link down the chain and // its clone at the same time. The first event should be handled normally, whilethe // second event is a duplicate and should be silently ignored. if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); // Need to call FinalizeCreation to create the new WicSourceHandle, but only in some // circumstances. If this BitmapSource isn't done initializing, then there's no need // to call FinalizeCreation since it will be called in EndInit. // FinalizeCreation makes use of properties on the object, and can throw if they're // not set properly. Use IsValidForFinalizeCreation to validate, but don't throw // if the validation fails. if (_bitmapInit.IsInitAtLeastOnce && IsValidForFinalizeCreation(/* throwIfInvalid = */ false)) { // FinalizeCreation() can throw because it usually makes pinvokes to things // that return HRESULTs. Since firing the download events up the chain is // new behavior in 4.0, these exceptions are breaking plus they aren't catchable // by user code. This is mostly here for ColorConvertedBitmap throwing with // a bad color context, but the fact that any BitmapSource could throw and // it wouldn't be catchable is justification enough to eat them all for now... // // See Dev10 #770180 try { FinalizeCreation(); _needsUpdate = true; } catch { } _downloadEvent.InvokeEvents(this, e); } } } private void OnSourceDownloadFailed(object sender, ExceptionEventArgs e) { // _weakBitmapSourceEventSink might be null. If the link down the chain was cloned, then // this link's event listeners would be cloned as well. As a result, this BitmapSource // will be listening to both the original and the clone's events, so it may get the event // twice. // TransformedBitmap --> CachedBitmap // | // | (caused by cloning the event helper on CachedBitmap) // +-------> CachedBitmap (clone) // So this BitmapSource will get DownloadFailed from both the link down the chain and // its clone at the same time. The first event should be handled normally, whilethe // second event is a duplicate and should be silently ignored. if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); _failedEvent.InvokeEvents(this, e); } } private void OnSourceDownloadProgress(object sender, DownloadProgressEventArgs e) { _progressEvent.InvokeEvents(this, e); } private void CleanUpWeakEventSink() { // Situation: // +------+ --a--> +-------------------+ --c--> +------------------+ // | this | | (weak event sink) | | nextBitmapSource | // +------+ <--b-- +-------------------+ <--d-- +------------------+ // | ^ // +---------------------e----------------------------+ // a is the _weakBitmapSourceEventSink reference // b is a WeakReference from _weakBitmapSourceEventSink back to this // c is the WeakBitmapSourceEventSink.EventSource property // d is the implicit reference due to _weakBitmapSourceEventSink attaching a handler // to nextBitmapSource // e is the Source property on TransformedBitmap, etc // nextBitmapSource fired DownloadCompleted/Failed, so we're detaching the event // handlers and cleaning up _weakBitmapSourceEventSink // Remove link c, link d // Remove the reference from the weak event sink // This implicitly removes link d as well (the EventSource setter detaches from the // old event source) // Note: this is NOT redundant, even if you detach explicitly somewhere else // If nextBitmapSource ever gets cloned, then the cloned nextBitmapSource will // contain the same delegates as nextBitmapSource, which includes the weak // event sink. So although we'll be cleaning up link a, the cloned // nextBitmapSource will still have a strong reference back to the weak // event sink _weakBitmapSourceEventSink.EventSource = null; // Remove link a _weakBitmapSourceEventSink = null; // Link b is a WeakReference and will not cause leaks // After: // +------+ +-------------------+ +------------------+ // | this | | (weak event sink) | | nextBitmapSource | // +------+ <--b-- +-------------------+ +------------------+ // | ^ // +---------------------e----------------------------+ // The weak event sink can be collected, unless a cloned nextBitmapSource is // still referencing it. // this can be collected if it's not being explicitly referenced // nextBitmapSource can be collected if it/this isn't being explicitly referenced } // Needs to be internal, called by subclasses to attach handlers when their Source changes internal void RegisterDownloadEventSource(BitmapSource eventSource) { if (_weakBitmapSourceEventSink == null) { _weakBitmapSourceEventSink = new WeakBitmapSourceEventSink(this); } _weakBitmapSourceEventSink.EventSource = eventSource; } // Needs to be internal, called by BitmapImage to detach handlers from the dummy downloading // BitmapFrameDecode's chain internal void UnregisterDownloadEventSource() { if (_weakBitmapSourceEventSink != null) { CleanUpWeakEventSink(); } } // Sometimes FinalizeCreation can't be called (e.g. when a ColorConvertedBitmap has no // SourceColorContext or no DestinationColorContext). EndInit needs to make these // checks before it calls FinalizeCreation, but so does OnSourceDownloadCompleted. // Therefore, the validation has been factored out into this method. // If the validation fails in EndInit, an exception should be thrown. If the validation // fails in OnSourceDownloadCompleted, there should be no exception. Therefore, the // throwIfInvalid flag is provided. internal virtual bool IsValidForFinalizeCreation(bool throwIfInvalid) { return true; } #endregion // // Workaround for a behavior change caused by a bug fix // // According to the old implementation, CopyCommon will clone the download events as // well (_asdfEvent = sourceBitmap._asdfEvent.Clone();). The problem with this is the // _asdfEvent.Clone() will also clone all the delegates currently attached to _asdfEvent, // so anyone listening to the original would be implicitly listening to the clone as well. // // The bug was that BitmapFrameDecode's clone was broken if it was made while the original // BFD was still downloading. The clone never does anything, so it doesn't update to show // the image after download completes and doesn't fire any events. So the net effect is // that anyone attached to the original BFD would still see only 1 event get fired despite // implicitly listening to the clone as well. // // The problem comes in when the BFD clone got fixed. It'll now update to show the image, // but it will also start firing events. So people listening to the original BFD will now // see a behavior change (used to get download only once, but will now get download from // the fixed clone as well). This flag is introduced as a workaround. When it's true, // CopyCommon works as it did before: delegates get copied. When it's false, delegates do // not get copied. BitmapFrameDecode will override this to return false in order to // simulate the old BFD behavior when clones didn't fire and the listeners on the original // only got the event once. // internal virtual bool ShouldCloneEventDelegates { get { return true; } } #endregion #region Animatable and Freezable ////// Called by the base Freezable class to make this object /// frozen. /// protected override bool FreezeCore(bool isChecking) { if (!base.FreezeCore(isChecking)) { return false; } if (IsDownloading) { return false; } return true; } ////// Copy the fields not covered by DPs. This is used by /// CloneCore(), CloneCurrentValueCore(), GetAsFrozenCore() and /// GetCurrentValueAsFrozenCore(). /// ////// SecurityCritical: This code accesses unmanaged resources /// SecurityTreatAsSafe: Inputs are verified /// [SecurityCritical, SecurityTreatAsSafe] private void CopyCommon(BitmapSource sourceBitmap) { _useVirtuals = sourceBitmap._useVirtuals; _delayCreation = sourceBitmap.DelayCreation; _creationComplete = sourceBitmap.CreationCompleted; WicSourceHandle = sourceBitmap.WicSourceHandle; // always do this near the top _syncObject = sourceBitmap.SyncObject; IsSourceCached = sourceBitmap.IsSourceCached; // // Decide on whether to stick with the old behavior of cloning delegates attached to // the events or not depending on the ShouldCloneEventDelegates property. It always // returns true, except in BitmapFrameDecode where it's overridden to return false. // See the comments for ShouldCloneEventDelegates for more details. // if (ShouldCloneEventDelegates) { if (sourceBitmap._downloadEvent != null) { _downloadEvent = sourceBitmap._downloadEvent.Clone(); } if (sourceBitmap._progressEvent != null) { _progressEvent = sourceBitmap._progressEvent.Clone(); } if (sourceBitmap._failedEvent != null) { _failedEvent = sourceBitmap._failedEvent.Clone(); } if (sourceBitmap._decodeFailedEvent != null) { _decodeFailedEvent = sourceBitmap._decodeFailedEvent.Clone(); } } // // else do nothing // the events are already created (they're initialized in the field declarations) // _format = sourceBitmap.Format; _pixelWidth = sourceBitmap.PixelWidth; _pixelHeight = sourceBitmap.PixelHeight; _dpiX = sourceBitmap.DpiX; _dpiY = sourceBitmap.DpiY; _palette = sourceBitmap.Palette; // // If a BitmapSource is part of a chain, then it will have handlers attached // to the BitmapSource down the chain, X. When X gets cloned, the delegates // in its events get cloned as well, so now the original BitmapSource will have // handlers attached both to X and the X's clone. Here, have the BitmapSource // detach from X's clone. // Note that since DependencyProperties are cloned first and since Source is a // DependencyProperty, the clone's Source would already be set at this point, // and the SourcePropertyChangedHook would have created a _weakBitmapSourceEventSink // object. // if (_weakBitmapSourceEventSink != null && sourceBitmap._weakBitmapSourceEventSink != null) { sourceBitmap._weakBitmapSourceEventSink.DetachSourceDownloadHandlers( _weakBitmapSourceEventSink.EventSource); } } ////// Implementation of protected override void CloneCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.CloneCore . ////// Implementation of protected override void CloneCurrentValueCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.CloneCurrentValueCore . ////// Implementation of protected override void GetAsFrozenCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } ///Freezable.GetAsFrozenCore . ////// Implementation of protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { BitmapSource sourceBitmap = (BitmapSource)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } #endregion #region Data Members private bool _delayCreation = false; private bool _creationComplete = false; private bool _useVirtuals = false; internal BitmapInitialize _bitmapInit = new BitmapInitialize(); ///Freezable.GetCurrentValueAsFrozenCore . ////// Critical: Unmanaged IWICBitmapSource handle. Access only through WicSourceHandle /// [SecurityCritical] internal BitmapSourceSafeMILHandle _wicSource = null; ////// Critical: Unmanaged IWICBitmapSource handle. /// [SecurityCritical] internal BitmapSourceSafeMILHandle _convertedDUCEPtr; internal object _syncObject; internal bool _isSourceCached; // Setting this to true causes us to throw away the old DUCECompatiblePtr which contains // a cache of the bitmap in video memory. We'll create a new DUCECompatiblePtr and // eventually send the bitmap to video memory again. internal bool _needsUpdate; internal bool _isColorCorrected; internal UniqueEventHelper _downloadEvent = new UniqueEventHelper(); internal UniqueEventHelper_progressEvent = new UniqueEventHelper (); internal UniqueEventHelper _failedEvent = new UniqueEventHelper (); internal UniqueEventHelper _decodeFailedEvent = new UniqueEventHelper (); // cached properties. should always reflect the unmanaged copy internal PixelFormat _format = PixelFormats.Default; internal int _pixelWidth = 0; internal int _pixelHeight = 0; internal double _dpiX = 96.0; internal double _dpiY = 96.0; internal BitmapPalette _palette = null; /// Duce resource internal DUCE.MultiChannelResource _duceResource = new DUCE.MultiChannelResource(); /// List of supported DUCE formats /// NOTE: Please add formats in increasing bpp order private readonly static PixelFormat[] s_supportedDUCEFormats = new PixelFormat[13] { PixelFormats.Indexed1, PixelFormats.BlackWhite, PixelFormats.Indexed2, PixelFormats.Gray2, PixelFormats.Indexed4, PixelFormats.Gray4, PixelFormats.Indexed8, PixelFormats.Gray8, PixelFormats.Bgr555, PixelFormats.Bgr565, PixelFormats.Bgr32, PixelFormats.Bgra32, PixelFormats.Pbgra32 }; // For propagating events in bitmap chains private WeakBitmapSourceEventSink _weakBitmapSourceEventSink = null; #endregion #region Interface definitions //************************************************************* // // IWICBitmapSource // //************************************************************* // Guid: IID_IWICBitmapSource [ComImport(), Guid("00000120-a8f2-4877-ba0a-fd2b6645fb94"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] internal interface IWICBitmapSource { [PreserveSig] int GetSize( out int puiWidth, out int puiHeight ); [PreserveSig] int GetPixelFormat( out Guid guidFormat ); [PreserveSig] int GetResolution( out double pDpiX, out double pDpiY ); [PreserveSig] int GetPalette( IntPtr /* IWICPalette */ pIPalette ); // SizeParamIndex says which parameter specifies the size of the array, // which we are saying is cbStride. [PreserveSig] int CopyPixels( IntPtr prc, int cbStride, int cbPixels, IntPtr pvPixels ); } #endregion #region Managed Bitmap Source //************************************************************** // // ManagedBitmapSource // //************************************************************* [ClassInterface(ClassInterfaceType.None)] internal class ManagedBitmapSource : IWICBitmapSource { private BitmapSource _bitmapSource; private ManagedBitmapSource() { } public ManagedBitmapSource(BitmapSource bitmapSource) { if (bitmapSource == null) { throw new System.ArgumentNullException("bitmapSource"); } _bitmapSource = bitmapSource; } int IWICBitmapSource.GetSize(out int puiWidth, out int puiHeight) { puiWidth = _bitmapSource.PixelWidth; puiHeight = _bitmapSource.PixelHeight; return NativeMethods.S_OK; } int IWICBitmapSource.GetPixelFormat(out Guid guidFormat) { guidFormat = _bitmapSource.Format.Guid; return NativeMethods.S_OK; } int IWICBitmapSource.GetResolution(out double pDpiX, out double pDpiY) { pDpiX = _bitmapSource.DpiX; pDpiY = _bitmapSource.DpiY; return NativeMethods.S_OK; } /// /// Critical - Calls into a P/Invoke method InitializeFromPalette(). /// [SecurityCritical] int IWICBitmapSource.GetPalette(IntPtr /* IWICPalette */ pIPalette) { BitmapPalette palette = _bitmapSource.Palette; if ((palette == null) || (palette.InternalPalette == null) || (SecurityHelper.SafeHandleIsInvalid(palette.InternalPalette))) { return (int)WinCodecErrors.WINCODEC_ERR_PALETTEUNAVAILABLE; } HRESULT.Check(UnsafeNativeMethods.WICPalette.InitializeFromPalette(pIPalette, palette.InternalPalette)); return NativeMethods.S_OK; } ////// Critical: This code can be used to expose pixel information /// [SecurityCritical] int IWICBitmapSource.CopyPixels(IntPtr prc, int cbStride, int cbPixels, IntPtr pvPixels) { if (cbStride < 0) { return NativeMethods.E_INVALIDARG; } if (pvPixels == IntPtr.Zero) { return NativeMethods.E_INVALIDARG; } Int32Rect rc; if (prc == IntPtr.Zero) { rc = new Int32Rect(0, 0, _bitmapSource.PixelWidth, _bitmapSource.PixelHeight); } else { rc = (Int32Rect)Marshal.PtrToStructure(prc, typeof(Int32Rect)); } int rectHeight, rectWidth; rectHeight = rc.Height; rectWidth = rc.Width; if (rc.Width < 1 || rc.Height < 1) { return NativeMethods.E_INVALIDARG; } // assuming cbStride can't be negative, but that prc.Height can PixelFormat pfStruct = _bitmapSource.Format; if (pfStruct.Format == PixelFormatEnum.Default || pfStruct.Format == PixelFormatEnum.Extended) { return (int)(WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDPIXELFORMAT); } int rectRowSize = checked((rectWidth * pfStruct.InternalBitsPerPixel + 7) / 8); if (cbPixels < checked((rectHeight - 1) * cbStride + rectRowSize)) { return (int)(WinCodecErrors.WINCODEC_ERR_INSUFFICIENTBUFFER); } // Need to marshal int arraySize = checked(rectHeight * rectRowSize); byte[] managedArray = new byte[arraySize]; // perform the copy _bitmapSource.CopyPixels(rc, managedArray, rectRowSize, 0); { // transfer the contents of the relevant rect from the managed array to pvPixels long rowPtr = pvPixels.ToInt64(); for (int y = 0; y < rectHeight; y++) { Marshal.Copy(managedArray, y * rectRowSize, new IntPtr(rowPtr), rectRowSize); rowPtr += cbStride; } } return NativeMethods.S_OK; } } #endregion #region WeakBitmapSourceEventSink // Used to propagate DownloadCompleted events for bitmap chains // The simple way is to just have this BitmapSource add handlers to the chained // BitmapSource's DownloadCompleted/DownloadFailed event. But that creates // an implicit strong reference from the child back to this. Since multiple chains can // contain the child, this BitmapSource can prevent BitmapSources in another chain // from being collected. // (A) <-----------------> (B) // (C) <--> (D) <--> (E) <--^ // A existing prevents C's entire chain from being collected // The weak event sink prevents a strong reference from the chained BitmapSource // back to this one. // (A) --> (weak) <--> (B) private class WeakBitmapSourceEventSink : WeakReference { public WeakBitmapSourceEventSink(BitmapSource bitmapSource) : base(bitmapSource) { } public void OnSourceDownloadCompleted(object sender, EventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadCompleted(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void OnSourceDownloadFailed(object sender, ExceptionEventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadFailed(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void OnSourceDownloadProgress(object sender, DownloadProgressEventArgs e) { BitmapSource bitmapSource = this.Target as BitmapSource; if (null != bitmapSource) { bitmapSource.OnSourceDownloadProgress(bitmapSource, e); } else { DetachSourceDownloadHandlers(EventSource); } } public void DetachSourceDownloadHandlers(BitmapSource source) { if (!source.IsFrozen) { source.DownloadCompleted -= OnSourceDownloadCompleted; source.DownloadFailed -= OnSourceDownloadFailed; source.DownloadProgress -= OnSourceDownloadProgress; } } public void AttachSourceDownloadHandlers() { if (!_eventSource.IsFrozen) { _eventSource.DownloadCompleted += OnSourceDownloadCompleted; _eventSource.DownloadFailed += OnSourceDownloadFailed; _eventSource.DownloadProgress += OnSourceDownloadProgress; } } private BitmapSource _eventSource; public BitmapSource EventSource { get { return _eventSource; } set { if (_eventSource != null) { DetachSourceDownloadHandlers(_eventSource); } _eventSource = value; if (_eventSource != null) { AttachSourceDownloadHandlers(); } } } } #endregion } #endregion // BitmapSource } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- MappingItemCollection.cs
- GenericUriParser.cs
- GridErrorDlg.cs
- RegistrationServices.cs
- SkewTransform.cs
- StickyNoteHelper.cs
- precedingquery.cs
- ZipIOExtraFieldZip64Element.cs
- CollectionConverter.cs
- ParameterReplacerVisitor.cs
- SwitchLevelAttribute.cs
- ImageMapEventArgs.cs
- DecimalSumAggregationOperator.cs
- PEFileEvidenceFactory.cs
- PaginationProgressEventArgs.cs
- CompositeKey.cs
- ContextMenuStripActionList.cs
- CompiledRegexRunnerFactory.cs
- FloatUtil.cs
- XamlFxTrace.cs
- DesignBindingValueUIHandler.cs
- ContainerControl.cs
- WindowsTokenRoleProvider.cs
- XmlSchemaElement.cs
- followingsibling.cs
- IndentTextWriter.cs
- TemplateField.cs
- RelatedCurrencyManager.cs
- DefaultHttpHandler.cs
- SqlConnectionPoolProviderInfo.cs
- Comparer.cs
- RequestCacheEntry.cs
- StreamingContext.cs
- HandlerBase.cs
- TextChangedEventArgs.cs
- SqlParameter.cs
- ForeignConstraint.cs
- BuildProvider.cs
- WorkflowItemsPresenter.cs
- ExcludeFromCodeCoverageAttribute.cs
- ProtocolsConfiguration.cs
- CodePrimitiveExpression.cs
- ClientOptions.cs
- UniqueIdentifierService.cs
- XhtmlBasicObjectListAdapter.cs
- CancelRequestedRecord.cs
- EventData.cs
- TypeConverterAttribute.cs
- MdiWindowListStrip.cs
- PictureBox.cs
- FtpWebRequest.cs
- SymLanguageType.cs
- DropShadowBitmapEffect.cs
- CollectionDataContractAttribute.cs
- PtsContext.cs
- SegmentInfo.cs
- CollectionViewSource.cs
- OuterGlowBitmapEffect.cs
- DataGridViewRowsAddedEventArgs.cs
- InvokeBinder.cs
- TraceData.cs
- DetectRunnableInstancesTask.cs
- StateBag.cs
- Roles.cs
- ConfigurationPropertyCollection.cs
- NativeRecognizer.cs
- MD5HashHelper.cs
- _ScatterGatherBuffers.cs
- SqlServer2KCompatibilityCheck.cs
- SimpleBitVector32.cs
- WebPartDisplayModeEventArgs.cs
- Propagator.JoinPropagator.cs
- TreeChangeInfo.cs
- ToolStripLabel.cs
- JsonFormatReaderGenerator.cs
- BitmapImage.cs
- RecordConverter.cs
- NonPrimarySelectionGlyph.cs
- ResolveInfo.cs
- ActivityExecutorSurrogate.cs
- CacheAxisQuery.cs
- FixedSOMSemanticBox.cs
- DnsPermission.cs
- ImageList.cs
- DataGridViewDesigner.cs
- altserialization.cs
- DataServiceRequestOfT.cs
- ApplicationSettingsBase.cs
- SequentialWorkflowRootDesigner.cs
- SHA384Cng.cs
- BindingSource.cs
- AuthStoreRoleProvider.cs
- ContentElement.cs
- SqlStream.cs
- HtmlInputText.cs
- XmlImplementation.cs
- ALinqExpressionVisitor.cs
- CodePageUtils.cs
- IntegrationExceptionEventArgs.cs
- QueryStringParameter.cs