Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / System / Windows / Media / Imaging / WriteableBitmap.cs / 5 / WriteableBitmap.cs
//------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All Rights Reserved. // // File: WriteableBitmap.cs //----------------------------------------------------------------------------- using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using MS.Win32.PresentationCore; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using MS.Internal.PresentationCore; // SecurityHelper using System.Threading; namespace System.Windows.Media.Imaging { ////// WriteableBitmap provides an efficient, tear-free mechanism for updating /// a system-memory bitmap. /// public sealed class WriteableBitmap : System.Windows.Media.Imaging.BitmapSource { #region Constructors ////// Internal constructor /// internal WriteableBitmap() { } ////// Creates a new WriteableBitmap instance initialized with the /// contents of the specified BitmapSource. /// /// /// The BitmapSource to copy. /// ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] public WriteableBitmap( BitmapSource source ) : base(true) // Use base class virtuals { InitFromBitmapSource(source); } ////// Initializes a new instance of the WriteableBitmap class with /// the specified parameters. /// /// The desired width of the bitmap. /// The desired height of the bitmap. /// The horizontal dots per inch (dpi) of the bitmap. /// The vertical dots per inch (dpi) of the bitmap. /// The PixelFormat of the bitmap. /// The BitmapPalette of the bitmap. ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] public WriteableBitmap( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, BitmapPalette palette ) : base(true) // Use base class virtuals { BeginInit(); // // Sanitize inputs // if (pixelFormat == null) { // Backwards Compat: // // The original code would null-ref, but we choose to raise a // better exception. throw new ArgumentNullException("pixelFormat"); } if (pixelFormat.Palettized && palette == null) { throw new InvalidOperationException(SR.Get(SRID.Image_IndexedPixelFormatRequiresPalette)); } if (pixelFormat.Format == PixelFormatEnum.Extended) { // We don't support third-party pixel formats yet. throw new ArgumentException(SR.Get(SRID.Effect_PixelFormat), "pixelFormat"); } if (pixelWidth < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (pixelWidth == 0) { // Backwards Compat HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } if (pixelHeight < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (pixelHeight == 0) { // Backwards Compat HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } // // Create and initialize a new unmanaged double buffered bitmap. // Guid formatGuid = pixelFormat.Guid; // This SafeMILHandle gets ignored if the pixel format is not palettized. SafeMILHandle internalPalette = new SafeMILHandle(); if (pixelFormat.Palettized) { internalPalette = palette.InternalPalette; } HRESULT.Check(MILSwDoubleBufferedBitmap.Create( (uint) pixelWidth, // safe cast (uint) pixelHeight, // safe cast dpiX, dpiY, ref formatGuid, internalPalette, out _pDoubleBufferedBitmap )); // Momentarily lock to populate the BackBuffer/BackBufferStride properties. Lock(); Unlock(); EndInit(); } #endregion // Constructors #region Public Methods ////// Adds a dirty region to the WriteableBitmap's back buffer. /// /// /// An Int32Rect structure specifying the dirty region. /// ////// This method can be called multiple times, and the areas are accumulated /// in a sufficient, but not necessarily minimal, representation. For efficiency, /// only the areas that are marked as dirty are guaranteed to be copied over to /// the rendering system. /// AddDirtyRect can only be called while the bitmap is locked, otherwise an /// InvalidOperationException will be thrown. /// ////// Critical: Accesses a handle to an unmanaged resource. /// PublicOK: Input dirty rect is sanitized before use. /// [SecurityCritical] public void AddDirtyRect(Int32Rect dirtyRect) { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } // // Sanitize the dirty rect. // if (dirtyRect.X < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Y < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Width < 0 || dirtyRect.Width > _pixelWidth) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (dirtyRect.Height < 0 || dirtyRect.Height > _pixelHeight) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } if (!dirtyRect.IsEmpty) { MILSwDoubleBufferedBitmap.AddDirtyRect( _pDoubleBufferedBitmap, ref dirtyRect); _hasDirtyRects = true; } // Note: we do not call WritePostscript because we do not want to // raise change notifications until the writeable bitmap is unlocked. } ////// Shadows inherited Copy() with a strongly typed version for convenience. /// public new WriteableBitmap Clone() { return (WriteableBitmap)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed version for convenience. /// public new WriteableBitmap CloneCurrentValue() { return (WriteableBitmap)base.CloneCurrentValue(); } ////// This method locks the WriteableBitmap and increments the lock count. /// ////// By "locking" the WriteableBitmap, updates will not be sent to the rendering system until /// the WriteableBitmap is fully unlocked. This can be used to support multi-threaded scenarios. /// This method blocks until the rendering system is finished processing the last frame's update. /// To provide a timeout see WriteableBitmap.TryLock. /// Locking the WriteableBitmap gives the caller write permission to the back buffer whose address /// can be obtained via the WriteableBitmap.BackBuffer property. /// public void Lock() { bool locked = TryLock(Duration.Forever); Debug.Assert(locked); } ////// This method tries to lock the WriteableBitmap for the specified /// timeout and increments the lock count if successful. /// /// /// The amount of time to wait while trying to acquire the lock. /// To block indefinitely pass Duration.Forever. /// Duration.Automatic is an invalid value. /// ///Returns true if the lock is now held, false otherwise. ////// Critical: Obtains and stores pointers to unmanaged memory. /// PublicOK: Pointers are not exposed to caller. This method /// should work from partial trust. /// [SecurityCritical] public bool TryLock(Duration timeout) { WritePreamble(); TimeSpan timeoutSpan; if (timeout == Duration.Automatic) { throw new ArgumentOutOfRangeException("timeout"); } else if (timeout == Duration.Forever) { timeoutSpan = TimeSpan.FromMilliseconds(-1); } else { timeoutSpan = timeout.TimeSpan; } if (_lockCount == UInt32.MaxValue) { throw new InvalidOperationException(SR.Get(SRID.Image_LockCountLimit)); } if (_lockCount == 0) { // Try to acquire the back buffer by the supplied timeout, if the acquire call times out, return false. if (!AcquireBackBuffer(timeoutSpan, true)) { return false; } Int32Rect rect = new Int32Rect(0, 0, _pixelWidth, _pixelHeight); HRESULT.Check(UnsafeNativeMethods.WICBitmap.Lock( WicSourceHandle, ref rect, LockFlags.MIL_LOCK_WRITE, out _pBackBufferLock )); // If this is the first lock operation, cache the BackBuffer and // BackBufferStride. These two values will never change, so we // don't fetch them on every lock. if (_backBuffer == IntPtr.Zero) { IntPtr tempBackBufferPointer = IntPtr.Zero; uint lockBufferSize = 0; HRESULT.Check(UnsafeNativeMethods.WICBitmapLock.GetDataPointer( _pBackBufferLock, ref lockBufferSize, ref tempBackBufferPointer )); BackBuffer = tempBackBufferPointer; uint lockBufferStride = 0; HRESULT.Check(UnsafeNativeMethods.WICBitmapLock.GetStride( _pBackBufferLock, ref lockBufferStride )); Invariant.Assert(lockBufferStride <= Int32.MaxValue); _backBufferStride.Value = (int)lockBufferStride; } // If we were subscribed to the CommittingBatch event, unsubscribe // since we should not be part of the batch now that we are // locked. When we unlock, we will subscribe to the // CommittingBatch again. UnsubscribeFromCommittingBatch(); } _lockCount++; return true; } ////// This method decrements the lock count, and if it reaches zero will release the /// on the back buffer and request a render pass. /// ////// Critical: Releases lock around unmanaged memory. /// PublicOK: Pointers are not exposed to caller. This method /// should work from partial trust. /// [SecurityCritical] public void Unlock() { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } Invariant.Assert(_lockCount > 0, "Lock count should never be negative!"); _lockCount--; if (_lockCount == 0) { // This makes the back buffer read-only. _pBackBufferLock.Dispose(); _pBackBufferLock = null; if (_hasDirtyRects) { SubscribeToCommittingBatch(); // // Notify listeners that we have changed. // WritePostscript(); } } } ////// Updates the pixels in the specified region of the bitmap. /// /// The rect to copy from the input buffer. /// The input buffer used to update the bitmap. /// The size of the input buffer in bytes. /// The stride of the input buffer in bytes. /// The destination x-coordinate of the left-most pixel to copy. /// The destination y-coordinate of the top-most pixel to copy. ////// Critical: Accesses critical code, performs unsafe operations. /// PublicOK: Demands unmanaged code permission. /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, IntPtr sourceBuffer, int sourceBufferSize, int sourceBufferStride, int destinationX, int destinationY ) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); WritePixelsImpl(sourceRect, sourceBuffer, sourceBufferSize, sourceBufferStride, destinationX, destinationY, /*backwardsCompat*/ false); } ////// Updates the pixels in the specified region of the bitmap. /// /// The rect to copy from the input buffer. /// The input buffer used to update the bitmap. /// The stride of the input buffer in bytes. /// The destination x-coordinate of the left-most pixel to copy. /// The destination y-coordinate of the top-most pixel to copy. ////// Critical: Calls critical methods, has unsafe code. /// PublicOK: The overall operation is safe, and the input is sanitized. /// This method should work from partial trust. /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, Array sourceBuffer, int sourceBufferStride, int destinationX, int destinationY ) { WritePreamble(); int elementSize; int sourceBufferSize; Type elementType; ValidateArrayAndGetInfo(sourceBuffer, /*backwardsCompat*/ false, out elementSize, out sourceBufferSize, out elementType); // We accept arrays of arbitrary value types - but not reference types. if (elementType == null || !elementType.IsValueType) { throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); } // Get the address of the data in the array by pinning it. GCHandle arrayHandle = GCHandle.Alloc(sourceBuffer, GCHandleType.Pinned); try { unsafe { IntPtr buffer = arrayHandle.AddrOfPinnedObject(); WritePixelsImpl(sourceRect, buffer, sourceBufferSize, sourceBufferStride, destinationX, destinationY, /*backwardsCompat*/ false); } } finally { arrayHandle.Free(); } } ////// Update the pixels of this Bitmap /// /// Area to update /// Input buffer /// Size of the buffer /// Stride ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] public unsafe void WritePixels( Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride ) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); if (bufferSize < 1) { throw new ArgumentOutOfRangeException("bufferSize", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (stride < 1) { throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (sourceRect.IsEmpty || sourceRect.Width <= 0 || sourceRect.Height <= 0) { return; } // Backwards-Compat: // // The "sourceRect" is actually a "destinationRect", as in it // refers to the location where the contents are written. // // This method presumes that the pixels are copied from the // the specified offset (element count) in the source buffer, and // that no sub-byte pixel formats are used. int destinationX = sourceRect.X; int destinationY = sourceRect.Y; sourceRect.X = 0; sourceRect.Y = 0; WritePixelsImpl(sourceRect, buffer, bufferSize, stride, destinationX, destinationY, /*backwardsCompat*/ true); } ////// Update the pixels of this Bitmap /// /// Area to update /// Input buffer /// Stride /// Input buffer offset ////// Critical - This method gets direct access to the input Array's memory. /// PublicOk - Input is a managed buffer which is safe, other inputs are safe as well /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, Array pixels, int stride, int offset ) { WritePreamble(); if (sourceRect.IsEmpty || sourceRect.Width <= 0 || sourceRect.Height <= 0) { return; } int elementSize; int sourceBufferSize; Type elementType; ValidateArrayAndGetInfo(pixels, /*backwardsCompat*/ true, out elementSize, out sourceBufferSize, out elementType); if (stride < 1) { throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.Get(SRID.ParameterCannotBeLessThan, 0)); } // We accept arrays of arbitrary value types - but not reference types. if (elementType == null || !elementType.IsValueType) { throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); } checked { int offsetInBytes = checked(offset * elementSize); if (offsetInBytes >= sourceBufferSize) { // Backwards compat: // // The original code would throw an exception deeper in // the code when it indexed off the end of the array. We // now check earlier (compat break) but throw the same // exception. throw new IndexOutOfRangeException(); } // Backwards-Compat: // // The "sourceRect" is actually a "destinationRect", as in it // refers to the location where the contents are written. // // This method presumes that the pixels are copied from the // the specified offset (element count) in the source buffer, and // that no sub-byte pixel formats are used. We handle the offset // later. int destinationX = sourceRect.X; int destinationY = sourceRect.Y; sourceRect.X = 0; sourceRect.Y = 0; // Get the address of the data in the array by pinning it. GCHandle arrayHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned); try { IntPtr buffer = arrayHandle.AddrOfPinnedObject(); checked { buffer = new IntPtr(((long) buffer) + (long) offsetInBytes); sourceBufferSize -= offsetInBytes; } WritePixelsImpl(sourceRect, buffer, sourceBufferSize, stride, destinationX, destinationY, /*backwardsCompat*/ true); } finally { arrayHandle.Free(); } } } #endregion // Public Methods #region Protected Methods ////// Implementation of Freezable.CreateInstanceCore. /// ///The new Freezable. protected override Freezable CreateInstanceCore() { return new WriteableBitmap(); } ////// Implementation of Freezable.CloneCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces clones of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void CloneCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap) sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.FreezeCore. /// ////// Critical: Accesses critical code. (AcquireBackBuffer, FreezeBackBuffer) /// TreatAsSafe: Method gets the back buffer, but doesn't expose it. /// [SecurityCritical, SecurityTreatAsSafe] protected override bool FreezeCore(bool isChecking) { bool canFreeze = (_lockCount == 0) && base.FreezeCore(isChecking); if (canFreeze && !isChecking) { Debug.Assert(_pBackBufferLock == null); // // By entering 'frozen' mode, we convert from being a // DoubleBufferedBitmap to a regular BitmapSource. // // Protect the back buffer for writing HRESULT.Check(MILSwDoubleBufferedBitmap.ProtectBackBuffer(_pDoubleBufferedBitmap)); // Get the back buffer to be used as our WicSourceHandle AcquireBackBuffer(TimeSpan.Zero, false); _needsUpdate = true; _hasDirtyRects = false; // From here on out we're going to effectively be an ordinary // BitmapSource. _actLikeSimpleBitmap = true; // Pull this resource off all the channels and put it back on. int channelCount = _duceResource.GetChannelCount(); for (int i = 0; i < channelCount; i++) { DUCE.IResource resource = this as DUCE.IResource; DUCE.Channel channel = _duceResource.GetChannel(i); // // It could have been added multiple times, so release until // it's no longer on a channel. // uint refCount = _duceResource.GetRefCountOnChannel(channel); for (uint j = 0; j < refCount; j++) { resource.ReleaseOnChannel(channel); } // Put it back on the Channel, only this time it wont // be a SwDoubleBufferedBitmap. for (uint j = 0; j < refCount; j++) { resource.AddRefOnChannel(channel); } } Debug.Assert(!_isWaitingForCommit); // We no longer need the SwDoubleBufferedBitmap _pDoubleBufferedBitmap.Dispose(); _pDoubleBufferedBitmap = null; // We will no longer need to wait for this event. _copyCompletedEvent.Close(); _copyCompletedEvent = null; // Clear out unused variables _committingBatchHandler = null; _pBackBuffer = null; } return canFreeze; } ////// Implementation of Freezable.CloneCurrentValueCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces clones of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void CloneCurrentValueCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap) sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.GetAsFrozenCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces GetAsFrozen of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void GetAsFrozenCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.GetCurrentValueAsFrozenCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces GetCurrentValueAsFrozen of original image. /// [SecurityCritical, SecurityTreatAsSafe] protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } #endregion // Protected Methods #region Private/Internal Methods ////// Initializes this WriteableBitmap with the /// contents of the specified BitmapSource. /// /// /// The BitmapSource to copy. /// ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] private void InitFromBitmapSource( BitmapSource source ) { if (source == null) { throw new ArgumentNullException("source"); } if (source.PixelWidth < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (source.PixelHeight < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } BeginInit(); _syncObject = source.SyncObject; lock (_syncObject) { Guid formatGuid = source.Format.Guid; SafeMILHandle internalPalette = new SafeMILHandle(); if (source.Format.Palettized) { internalPalette = source.Palette.InternalPalette; } HRESULT.Check(MILSwDoubleBufferedBitmap.Create( (uint)source.PixelWidth, // safe cast (uint)source.PixelHeight, // safe cast source.DpiX, source.DpiY, ref formatGuid, internalPalette, out _pDoubleBufferedBitmap )); Lock(); Int32Rect rcFull = new Int32Rect(0, 0, _pixelWidth, _pixelHeight); int bufferSize = checked(_backBufferStride.Value * source.PixelHeight); source.CriticalCopyPixels(rcFull, _backBuffer, bufferSize, _backBufferStride.Value); AddDirtyRect(rcFull); Unlock(); } EndInit(); } ////// Updates the pixels in the specified region of the bitmap. /// /// /// The rect to copy from the input buffer. /// /// /// The input buffer used to update the bitmap. /// /// /// The size of the input buffer in bytes. /// /// /// The stride of the input buffer in bytes. /// /// /// The destination x-coordinate of the left-most pixel to copy. /// /// /// The destination y-coordinate of the top-most pixel to copy. /// /// /// Whether or not to preserve the old WritePixels behavior. /// ////// Critical: Accesses critical code, performs unsafe operations. /// [SecurityCritical] private void WritePixelsImpl( Int32Rect sourceRect, IntPtr sourceBuffer, int sourceBufferSize, int sourceBufferStride, int destinationX, int destinationY, bool backwardsCompat ) { // // Sanitize the source rect and assure it will fit within the back buffer. // if (sourceRect.X < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (sourceRect.Y < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (sourceRect.Width < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (sourceRect.Width > _pixelWidth) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } } if (sourceRect.Height < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } if (sourceRect.Height > _pixelHeight) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } } if (destinationX < 0) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } } if (destinationX > _pixelWidth - sourceRect.Width) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("destinationX", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth - sourceRect.Width)); } } if (destinationY < 0) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } else { throw new ArgumentOutOfRangeException("destinationY", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight - sourceRect.Height)); } } if (destinationY > _pixelHeight - sourceRect.Height) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("destinationY", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight - sourceRect.Height)); } } // // Sanitize the other parameters. // if (sourceBuffer == IntPtr.Zero) { // Backwards Compat: // // The original code would null-ref when it was passed a null // buffer (IntPtr.Zero). We choose to throw a better // exception. throw new ArgumentNullException(backwardsCompat ? "buffer" : "sourceBuffer"); } if (sourceBufferStride < 1) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceBufferStride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (sourceRect.Width == 0 || sourceRect.Height == 0) { Debug.Assert(!backwardsCompat); // Nothing to do. return; } checked { uint finalRowWidthInBits = (uint)((sourceRect.X + sourceRect.Width) * _format.InternalBitsPerPixel); uint finalRowWidthInBytes = ((finalRowWidthInBits + 7) / 8); uint requiredBufferSize = (uint)((sourceRect.Y + sourceRect.Height - 1) * sourceBufferStride) + finalRowWidthInBytes; if (sourceBufferSize < requiredBufferSize) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_INSUFFICIENTBUFFER); } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBufferSize), "sourceBufferSize"); } } uint copyWidthInBits = (uint)(sourceRect.Width * _format.InternalBitsPerPixel); // Calculate some offsets that we'll need in a moment. uint sourceXbyteOffset = (uint)((sourceRect.X * _format.InternalBitsPerPixel) / 8); uint sourceBufferBitOffset = (uint)((sourceRect.X * _format.InternalBitsPerPixel) % 8); uint firstPixelByteOffet = (uint)((sourceRect.Y * sourceBufferStride) + sourceXbyteOffset); uint destXbyteOffset = (uint)((destinationX * _format.InternalBitsPerPixel) / 8); uint destBufferBitOffset = (uint)((destinationX * _format.InternalBitsPerPixel) % 8); Int32Rect destinationRect = sourceRect; destinationRect.X = destinationX; destinationRect.Y = destinationY; // // Copy pixel information from the user supplied buffer to the back buffer. // unsafe { uint destOffset = (uint)(destinationY * _backBufferStride.Value) + destXbyteOffset; byte* pDest = (byte*)_backBuffer.ToPointer(); pDest += destOffset; uint outputBufferSize = _backBufferSize - destOffset; byte* pSource = (byte*)sourceBuffer.ToPointer(); pSource += firstPixelByteOffet; uint inputBufferSize = (uint)sourceBufferSize - firstPixelByteOffet; Lock(); MILUtilities.MILCopyPixelBuffer( pDest, outputBufferSize, (uint) _backBufferStride.Value, destBufferBitOffset, pSource, inputBufferSize, (uint) sourceBufferStride, sourceBufferBitOffset, (uint) sourceRect.Height, copyWidthInBits); AddDirtyRect(destinationRect); Unlock(); } } // Note: we do not call WritePostscript because we do not want to // raise change notifications until the writeable bitmap is unlocked. // // Change notifications may have already been raised in the Unlock // call in this method. } ////// Try to acquire the back buffer of our unmanaged double buffered bitmap in the specified timeout. /// /// /// The time to wait while trying to acquire the lock. /// /// /// Should we try to wait for the copy completed event? /// ///Returns true if the back buffer was acquired before the timeout expired. ////// Critical: Accesses a handle to an unmanaged resource. /// [SecurityCritical] private bool AcquireBackBuffer(TimeSpan timeout, bool waitForCopy) { bool backBufferAcquired = false; // // Only get the back buffer from the unmanaged double buffered bitmap if this is our // first time being called since the last successful call to OnCommittingBatch. // OnCommittingBatch sets _pBackBuffer to null. // if (_pBackBuffer == null) { bool shouldGetBackBuffer = true; if (waitForCopy) { // If we have committed a copy-forward command, we need to wait // for the render thread to finish the copy before we can use // the back buffer. shouldGetBackBuffer = _copyCompletedEvent.WaitOne(timeout, false); } if (shouldGetBackBuffer) { MILSwDoubleBufferedBitmap.GetBackBuffer( _pDoubleBufferedBitmap, out _pBackBuffer, out _backBufferSize); _syncObject = WicSourceHandle = _pBackBuffer; backBufferAcquired = true; } } else { backBufferAcquired = true; } return backBufferAcquired; } ////// Common implementation for CloneCore(), CloneCurrentValueCore(), /// GetAsFrozenCore(), and GetCurrentValueAsFrozenCore(). /// /// The WriteableBitmap to copy from. ////// Critical: Accesses a handle to an unmanaged resource. /// [SecurityCritical] private void CopyCommon(WriteableBitmap sourceBitmap) { // Avoid Animatable requesting resource updates for invalidations // that occur during construction. Animatable_IsResourceInvalidationNecessary = false; _actLikeSimpleBitmap = false; // Create a SwDoubleBufferedBitmap and copy the sourceBitmap into it. InitFromBitmapSource(sourceBitmap); // The next invalidation will cause Animatable to register an // UpdateResource callback. Animatable_IsResourceInvalidationNecessary = true; } // ISupportInitialize ////// Prepare the bitmap to accept initialize paramters. /// private void BeginInit() { _bitmapInit.BeginInit(); } ////// Prepare the bitmap to accept initialize paramters. /// ////// Critical: Access critical resources. /// [SecurityCritical, SecurityTreatAsSafe] private void EndInit() { _bitmapInit.EndInit(); FinalizeCreation(); } ////// Create the unmanaged resources. /// ////// Critical - access critical resource /// [SecurityCritical] internal override void FinalizeCreation() { IsSourceCached = true; CreationCompleted = true; UpdateCachedSettings(); } ////// Get the size of the specified array and of the elements in it. /// /// /// The array to get info about. /// /// /// On output, will contain the size of the elements in the array. /// /// /// On output, will contain the size of the array. /// ////// Critical - Calls Marshal.SizeOf, which has a link demand for some reason. /// [SecurityCritical] private void ValidateArrayAndGetInfo(Array sourceBuffer, bool backwardsCompat, out int elementSize, out int sourceBufferSize, out Type elementType) { // // Assure that a valid pixels Array was provided. // if (sourceBuffer == null) { throw new ArgumentNullException(backwardsCompat ? "pixels" : "sourceBuffer"); } if (sourceBuffer.Rank == 1) { if (sourceBuffer.GetLength(0) <= 0) { if (backwardsCompat) { elementSize = 1; sourceBufferSize = 0; elementType = null; } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBuffer), "sourceBuffer"); } } else { checked { object exemplar = sourceBuffer.GetValue(0); elementSize = Marshal.SizeOf(exemplar); sourceBufferSize = sourceBuffer.GetLength(0) * elementSize; elementType = exemplar.GetType(); } } } else if (sourceBuffer.Rank == 2) { if (sourceBuffer.GetLength(0) <= 0 || sourceBuffer.GetLength(1) <= 0) { if (backwardsCompat) { elementSize = 1; sourceBufferSize = 0; elementType = null; } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBuffer), "sourceBuffer"); } } else { checked { object exemplar = sourceBuffer.GetValue(0,0); elementSize = Marshal.SizeOf(exemplar); sourceBufferSize = sourceBuffer.GetLength(0) * sourceBuffer.GetLength(1) * elementSize; elementType = exemplar.GetType(); } } } else { throw new ArgumentException(SR.Get(SRID.Collection_BadRank), backwardsCompat ? "pixels" : "sourceBuffer"); } } ////// Adds a reference to our DUCE resource on /// /// The channel we want to AddRef on. /// ///. /// /// The handle to our DoubleBufferedBitmap or BitmapSource handle. /// ////// We override this method because we use a different resource /// type than our base class does. This probably suggests that the /// base class should not presume the resource type, but it /// currently does. The base class uses TYPE_BITMAPSOURCE /// resources, and we use TYPE_DOUBLEBUFFEREDBITMAP resources. /// internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { // // If we're in BitmapSource mode, then just defer to the BitmapSource // implementation. // if (_actLikeSimpleBitmap) { return base.AddRefOnChannelCore(channel); } if (_duceResource.CreateOrAddRefOnChannel(channel, DUCE.ResourceType.TYPE_DOUBLEBUFFEREDBITMAP)) { // This is the first AddRef on this channel... // If we are being put onto the asynchronous compositor channel in // a dirty state, we need to subscribe to the CommittingBatch event. if (!channel.IsSynchronous && _hasDirtyRects) { SubscribeToCommittingBatch(); } AddRefOnChannelAnimations(channel); // The first time our resource is created on a channel, we need // to update it. We can skip "on channel" check since we // already know that the resource is on channel. UpdateResource(channel, true); } return _duceResource.GetHandle(channel); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { // This is the last release from this channel... // If we are being pulled off the asynchronous compositor channel // then unsubscribe from the CommittingBatch event. if (!channel.IsSynchronous) { UnsubscribeFromCommittingBatch(); } ReleaseOnChannelAnimations(channel); } } ////// Updates the double-buffered bitmap DUCE resource with a pointer to our acutal object. /// /// The channel to update the resource on. /// /// If this is true, we know we are on channel and don't need to explicitly check. /// ////// Critical: Accesses a handle to an unmanaged resource. Calls critical methods. /// TreateAsSafe: We allocate the double buffered bitmap ourself and the user can never /// get ahold of it. /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateBitmapSourceResource(DUCE.Channel channel, bool skipOnChannelCheck) { // // If we're in BitmapSource mode, then just defer to the BitmapSource // implementation. // if (_actLikeSimpleBitmap) { base.UpdateBitmapSourceResource(channel, skipOnChannelCheck); return; } // We override this method because we use a different resource type // than our base class does. This probably suggests that the base // class should not presume the resource type, but it currently // does. The base class uses TYPE_BITMAPSOURCE resources, and we // use TYPE_DOUBLEBUFFEREDBITMAP resources. // 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)) { DUCE.MILCMD_DOUBLEBUFFEREDBITMAP command; command.Type = MILCMD.MilCmdDoubleBufferedBitmap; command.Handle = _duceResource.GetHandle(channel); unsafe { command.SwDoubleBufferedBitmap = (UInt64) _pDoubleBufferedBitmap.DangerousGetHandle().ToPointer(); } command.UseBackBuffer = channel.IsSynchronous ? 1u : 0u; // // We need to ensure that this object stays alive while traveling over the channel // so we'll AddRef it here, and simply take over the reference on the other side. // UnsafeNativeMethods.MILUnknown.AddRef(_pDoubleBufferedBitmap); unsafe { channel.SendSecurityCriticalCommand( (byte*)&command, sizeof(DUCE.MILCMD_DOUBLEBUFFEREDBITMAP) ); } } } private void SubscribeToCommittingBatch() { // Only subscribe the the CommittingBatch event if we are on-channel. if (!_isWaitingForCommit) { MediaContext mediaContext = MediaContext.From(Dispatcher); if (_duceResource.IsOnChannel(mediaContext.Channel)) { mediaContext.CommittingBatch += CommittingBatchHandler; _isWaitingForCommit = true; } } } private void UnsubscribeFromCommittingBatch() { if (_isWaitingForCommit) { MediaContext mediaContext = MediaContext.From(Dispatcher); mediaContext.CommittingBatch -= CommittingBatchHandler; _isWaitingForCommit = false; } } ////// Send a packet on the DUCE.Channel telling our double-buffered bitmap resource /// to copy forward dirty regions from the back buffer to the front buffer. /// ////// For the packet to be sent, the user must have added a dirty region to the /// WriteableBitmap and there must be no outstanding locks. /// ////// Critical: Accesses critical methods and a handle to an unmanaged resource. /// TreatAsSafe: Inputs are supplied internally. /// [SecurityCritical, SecurityTreatAsSafe] private void OnCommittingBatch(object sender, EventArgs args) { Debug.Assert(_isWaitingForCommit); // How else are we here? UnsubscribeFromCommittingBatch(); Debug.Assert(_lockCount == 0); // How else are we here? Debug.Assert(_hasDirtyRects); // How else are we here? // Before using the back buffer again, we need to know when // the rendering thread has completed the copy. By setting // our back buffer pointer to null, we'll have to re-acquire // it the next time, which will wait for the copy to complete. _copyCompletedEvent.Reset(); _pBackBuffer = null; DUCE.Channel channel = sender as DUCE.Channel; Debug.Assert(_duceResource.IsOnChannel(channel)); // How else are we here? // We are going to pass an event in the command packet we send to // the composition thread. We need to make sure the event stays // alive in case we get collected before the composition thread // processes the packet. We do this by duplicating the event // handle, and the composition thread will close the handle after // signalling it. IntPtr hDuplicate; IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess(); if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle( hCurrentProc, _copyCompletedEvent.SafeWaitHandle, hCurrentProc, out hDuplicate, 0, false, MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS )) { throw new Win32Exception(); } DUCE.MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD command; command.Type = MILCMD.MilCmdDoubleBufferedBitmapCopyForward; command.Handle = _duceResource.GetHandle(channel); command.CopyCompletedEvent = (UInt64) hDuplicate.ToInt64(); unsafe { channel.SendSecurityCriticalCommand( (byte*)&command, sizeof(DUCE.MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD) ); } // We are committing the batch to the asynchronous compositor, // which will copy the rects forward. The copy will complete // before we can access the back buffer again. So, we consider // ourselves clean. _hasDirtyRects = false; } #endregion #region Properties ////// Read-only data pointer to the back buffer. /// ////// Critical: Accesses back buffer pointer. /// PublicOK: Demands unmanaged code permission. /// public IntPtr BackBuffer { [SecurityCritical] get { SecurityHelper.DemandUnmanagedCode(); ReadPreamble(); return _backBuffer; } [SecurityCritical] private set { _backBuffer = value; } } ////// Critical: The pointer to the back buffer pixels. /// [SecurityCritical] private IntPtr _backBuffer; ////// Critical: The size of the back buffer. Should never be exposed /// to external parties. /// [SecurityCritical] private uint _backBufferSize; ////// Read-only stride of the back buffer. /// public int BackBufferStride { get { ReadPreamble(); return _backBufferStride.Value; } } ////// Critical: Sets the back buffer stride, which is important for /// internal pointer arithmetic calculations. /// private SecurityCriticalDataForSet_backBufferStride; #endregion // Properties #region Fields /// /// Critical: The pointer to the IMILSwDoubleBufferedBitmap /// interface that manages the back and front buffers. /// [SecurityCritical] private SafeMILHandle _pDoubleBufferedBitmap; // CSwDoubleBufferedBitmap ////// Critical: The pointer to the IWICBitmapLock interface that /// provides access to the back buffer pointer. /// [SecurityCritical] private SafeMILHandle _pBackBufferLock; // IWICBitmapLock ////// Critical: The pointer to the IMILBitmap interface that is the /// back buffer. /// [SecurityCritical] private BitmapSourceSafeMILHandle _pBackBuffer; // IMILBitmap private uint _lockCount = 0; // Flags whether the user has added a dirty rect since the last CopyForward packet was sent. private bool _hasDirtyRects = true; // Flags whether a MediaContext.CommittingBatch handler has already been added. private bool _isWaitingForCommit = false; private ManualResetEvent _copyCompletedEvent = new ManualResetEvent(true); private EventHandler CommittingBatchHandler { get { if (_committingBatchHandler == null) { _committingBatchHandler = OnCommittingBatch; } return _committingBatchHandler; } } private EventHandler _committingBatchHandler; // = OnCommittingBatch (CS0236) private bool _actLikeSimpleBitmap = false; #endregion // Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All Rights Reserved. // // File: WriteableBitmap.cs //----------------------------------------------------------------------------- using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using MS.Internal; using MS.Win32.PresentationCore; using System.Security; using System.Security.Permissions; using System.Diagnostics; using System.Windows.Media; using System.Globalization; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Media.Animation; using System.Windows.Media.Composition; using SR=MS.Internal.PresentationCore.SR; using SRID=MS.Internal.PresentationCore.SRID; using MS.Internal.PresentationCore; // SecurityHelper using System.Threading; namespace System.Windows.Media.Imaging { ////// WriteableBitmap provides an efficient, tear-free mechanism for updating /// a system-memory bitmap. /// public sealed class WriteableBitmap : System.Windows.Media.Imaging.BitmapSource { #region Constructors ////// Internal constructor /// internal WriteableBitmap() { } ////// Creates a new WriteableBitmap instance initialized with the /// contents of the specified BitmapSource. /// /// /// The BitmapSource to copy. /// ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] public WriteableBitmap( BitmapSource source ) : base(true) // Use base class virtuals { InitFromBitmapSource(source); } ////// Initializes a new instance of the WriteableBitmap class with /// the specified parameters. /// /// The desired width of the bitmap. /// The desired height of the bitmap. /// The horizontal dots per inch (dpi) of the bitmap. /// The vertical dots per inch (dpi) of the bitmap. /// The PixelFormat of the bitmap. /// The BitmapPalette of the bitmap. ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] public WriteableBitmap( int pixelWidth, int pixelHeight, double dpiX, double dpiY, PixelFormat pixelFormat, BitmapPalette palette ) : base(true) // Use base class virtuals { BeginInit(); // // Sanitize inputs // if (pixelFormat == null) { // Backwards Compat: // // The original code would null-ref, but we choose to raise a // better exception. throw new ArgumentNullException("pixelFormat"); } if (pixelFormat.Palettized && palette == null) { throw new InvalidOperationException(SR.Get(SRID.Image_IndexedPixelFormatRequiresPalette)); } if (pixelFormat.Format == PixelFormatEnum.Extended) { // We don't support third-party pixel formats yet. throw new ArgumentException(SR.Get(SRID.Effect_PixelFormat), "pixelFormat"); } if (pixelWidth < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (pixelWidth == 0) { // Backwards Compat HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } if (pixelHeight < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (pixelHeight == 0) { // Backwards Compat HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } // // Create and initialize a new unmanaged double buffered bitmap. // Guid formatGuid = pixelFormat.Guid; // This SafeMILHandle gets ignored if the pixel format is not palettized. SafeMILHandle internalPalette = new SafeMILHandle(); if (pixelFormat.Palettized) { internalPalette = palette.InternalPalette; } HRESULT.Check(MILSwDoubleBufferedBitmap.Create( (uint) pixelWidth, // safe cast (uint) pixelHeight, // safe cast dpiX, dpiY, ref formatGuid, internalPalette, out _pDoubleBufferedBitmap )); // Momentarily lock to populate the BackBuffer/BackBufferStride properties. Lock(); Unlock(); EndInit(); } #endregion // Constructors #region Public Methods ////// Adds a dirty region to the WriteableBitmap's back buffer. /// /// /// An Int32Rect structure specifying the dirty region. /// ////// This method can be called multiple times, and the areas are accumulated /// in a sufficient, but not necessarily minimal, representation. For efficiency, /// only the areas that are marked as dirty are guaranteed to be copied over to /// the rendering system. /// AddDirtyRect can only be called while the bitmap is locked, otherwise an /// InvalidOperationException will be thrown. /// ////// Critical: Accesses a handle to an unmanaged resource. /// PublicOK: Input dirty rect is sanitized before use. /// [SecurityCritical] public void AddDirtyRect(Int32Rect dirtyRect) { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } // // Sanitize the dirty rect. // if (dirtyRect.X < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Y < 0) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (dirtyRect.Width < 0 || dirtyRect.Width > _pixelWidth) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (dirtyRect.Height < 0 || dirtyRect.Height > _pixelHeight) { throw new ArgumentOutOfRangeException("dirtyRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } if (!dirtyRect.IsEmpty) { MILSwDoubleBufferedBitmap.AddDirtyRect( _pDoubleBufferedBitmap, ref dirtyRect); _hasDirtyRects = true; } // Note: we do not call WritePostscript because we do not want to // raise change notifications until the writeable bitmap is unlocked. } ////// Shadows inherited Copy() with a strongly typed version for convenience. /// public new WriteableBitmap Clone() { return (WriteableBitmap)base.Clone(); } ////// Shadows inherited CloneCurrentValue() with a strongly typed version for convenience. /// public new WriteableBitmap CloneCurrentValue() { return (WriteableBitmap)base.CloneCurrentValue(); } ////// This method locks the WriteableBitmap and increments the lock count. /// ////// By "locking" the WriteableBitmap, updates will not be sent to the rendering system until /// the WriteableBitmap is fully unlocked. This can be used to support multi-threaded scenarios. /// This method blocks until the rendering system is finished processing the last frame's update. /// To provide a timeout see WriteableBitmap.TryLock. /// Locking the WriteableBitmap gives the caller write permission to the back buffer whose address /// can be obtained via the WriteableBitmap.BackBuffer property. /// public void Lock() { bool locked = TryLock(Duration.Forever); Debug.Assert(locked); } ////// This method tries to lock the WriteableBitmap for the specified /// timeout and increments the lock count if successful. /// /// /// The amount of time to wait while trying to acquire the lock. /// To block indefinitely pass Duration.Forever. /// Duration.Automatic is an invalid value. /// ///Returns true if the lock is now held, false otherwise. ////// Critical: Obtains and stores pointers to unmanaged memory. /// PublicOK: Pointers are not exposed to caller. This method /// should work from partial trust. /// [SecurityCritical] public bool TryLock(Duration timeout) { WritePreamble(); TimeSpan timeoutSpan; if (timeout == Duration.Automatic) { throw new ArgumentOutOfRangeException("timeout"); } else if (timeout == Duration.Forever) { timeoutSpan = TimeSpan.FromMilliseconds(-1); } else { timeoutSpan = timeout.TimeSpan; } if (_lockCount == UInt32.MaxValue) { throw new InvalidOperationException(SR.Get(SRID.Image_LockCountLimit)); } if (_lockCount == 0) { // Try to acquire the back buffer by the supplied timeout, if the acquire call times out, return false. if (!AcquireBackBuffer(timeoutSpan, true)) { return false; } Int32Rect rect = new Int32Rect(0, 0, _pixelWidth, _pixelHeight); HRESULT.Check(UnsafeNativeMethods.WICBitmap.Lock( WicSourceHandle, ref rect, LockFlags.MIL_LOCK_WRITE, out _pBackBufferLock )); // If this is the first lock operation, cache the BackBuffer and // BackBufferStride. These two values will never change, so we // don't fetch them on every lock. if (_backBuffer == IntPtr.Zero) { IntPtr tempBackBufferPointer = IntPtr.Zero; uint lockBufferSize = 0; HRESULT.Check(UnsafeNativeMethods.WICBitmapLock.GetDataPointer( _pBackBufferLock, ref lockBufferSize, ref tempBackBufferPointer )); BackBuffer = tempBackBufferPointer; uint lockBufferStride = 0; HRESULT.Check(UnsafeNativeMethods.WICBitmapLock.GetStride( _pBackBufferLock, ref lockBufferStride )); Invariant.Assert(lockBufferStride <= Int32.MaxValue); _backBufferStride.Value = (int)lockBufferStride; } // If we were subscribed to the CommittingBatch event, unsubscribe // since we should not be part of the batch now that we are // locked. When we unlock, we will subscribe to the // CommittingBatch again. UnsubscribeFromCommittingBatch(); } _lockCount++; return true; } ////// This method decrements the lock count, and if it reaches zero will release the /// on the back buffer and request a render pass. /// ////// Critical: Releases lock around unmanaged memory. /// PublicOK: Pointers are not exposed to caller. This method /// should work from partial trust. /// [SecurityCritical] public void Unlock() { WritePreamble(); if (_lockCount == 0) { throw new InvalidOperationException(SR.Get(SRID.Image_MustBeLocked)); } Invariant.Assert(_lockCount > 0, "Lock count should never be negative!"); _lockCount--; if (_lockCount == 0) { // This makes the back buffer read-only. _pBackBufferLock.Dispose(); _pBackBufferLock = null; if (_hasDirtyRects) { SubscribeToCommittingBatch(); // // Notify listeners that we have changed. // WritePostscript(); } } } ////// Updates the pixels in the specified region of the bitmap. /// /// The rect to copy from the input buffer. /// The input buffer used to update the bitmap. /// The size of the input buffer in bytes. /// The stride of the input buffer in bytes. /// The destination x-coordinate of the left-most pixel to copy. /// The destination y-coordinate of the top-most pixel to copy. ////// Critical: Accesses critical code, performs unsafe operations. /// PublicOK: Demands unmanaged code permission. /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, IntPtr sourceBuffer, int sourceBufferSize, int sourceBufferStride, int destinationX, int destinationY ) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); WritePixelsImpl(sourceRect, sourceBuffer, sourceBufferSize, sourceBufferStride, destinationX, destinationY, /*backwardsCompat*/ false); } ////// Updates the pixels in the specified region of the bitmap. /// /// The rect to copy from the input buffer. /// The input buffer used to update the bitmap. /// The stride of the input buffer in bytes. /// The destination x-coordinate of the left-most pixel to copy. /// The destination y-coordinate of the top-most pixel to copy. ////// Critical: Calls critical methods, has unsafe code. /// PublicOK: The overall operation is safe, and the input is sanitized. /// This method should work from partial trust. /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, Array sourceBuffer, int sourceBufferStride, int destinationX, int destinationY ) { WritePreamble(); int elementSize; int sourceBufferSize; Type elementType; ValidateArrayAndGetInfo(sourceBuffer, /*backwardsCompat*/ false, out elementSize, out sourceBufferSize, out elementType); // We accept arrays of arbitrary value types - but not reference types. if (elementType == null || !elementType.IsValueType) { throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); } // Get the address of the data in the array by pinning it. GCHandle arrayHandle = GCHandle.Alloc(sourceBuffer, GCHandleType.Pinned); try { unsafe { IntPtr buffer = arrayHandle.AddrOfPinnedObject(); WritePixelsImpl(sourceRect, buffer, sourceBufferSize, sourceBufferStride, destinationX, destinationY, /*backwardsCompat*/ false); } } finally { arrayHandle.Free(); } } ////// Update the pixels of this Bitmap /// /// Area to update /// Input buffer /// Size of the buffer /// Stride ////// Critical - access critical code, accepts pointer arguments /// PublicOK - demands unmanaged code permission /// [SecurityCritical] public unsafe void WritePixels( Int32Rect sourceRect, IntPtr buffer, int bufferSize, int stride ) { SecurityHelper.DemandUnmanagedCode(); WritePreamble(); if (bufferSize < 1) { throw new ArgumentOutOfRangeException("bufferSize", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (stride < 1) { throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (sourceRect.IsEmpty || sourceRect.Width <= 0 || sourceRect.Height <= 0) { return; } // Backwards-Compat: // // The "sourceRect" is actually a "destinationRect", as in it // refers to the location where the contents are written. // // This method presumes that the pixels are copied from the // the specified offset (element count) in the source buffer, and // that no sub-byte pixel formats are used. int destinationX = sourceRect.X; int destinationY = sourceRect.Y; sourceRect.X = 0; sourceRect.Y = 0; WritePixelsImpl(sourceRect, buffer, bufferSize, stride, destinationX, destinationY, /*backwardsCompat*/ true); } ////// Update the pixels of this Bitmap /// /// Area to update /// Input buffer /// Stride /// Input buffer offset ////// Critical - This method gets direct access to the input Array's memory. /// PublicOk - Input is a managed buffer which is safe, other inputs are safe as well /// [SecurityCritical] public void WritePixels( Int32Rect sourceRect, Array pixels, int stride, int offset ) { WritePreamble(); if (sourceRect.IsEmpty || sourceRect.Width <= 0 || sourceRect.Height <= 0) { return; } int elementSize; int sourceBufferSize; Type elementType; ValidateArrayAndGetInfo(pixels, /*backwardsCompat*/ true, out elementSize, out sourceBufferSize, out elementType); if (stride < 1) { throw new ArgumentOutOfRangeException("stride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (offset < 0) { throw new ArgumentOutOfRangeException("offset", SR.Get(SRID.ParameterCannotBeLessThan, 0)); } // We accept arrays of arbitrary value types - but not reference types. if (elementType == null || !elementType.IsValueType) { throw new ArgumentException(SR.Get(SRID.Image_InvalidArrayForPixel)); } checked { int offsetInBytes = checked(offset * elementSize); if (offsetInBytes >= sourceBufferSize) { // Backwards compat: // // The original code would throw an exception deeper in // the code when it indexed off the end of the array. We // now check earlier (compat break) but throw the same // exception. throw new IndexOutOfRangeException(); } // Backwards-Compat: // // The "sourceRect" is actually a "destinationRect", as in it // refers to the location where the contents are written. // // This method presumes that the pixels are copied from the // the specified offset (element count) in the source buffer, and // that no sub-byte pixel formats are used. We handle the offset // later. int destinationX = sourceRect.X; int destinationY = sourceRect.Y; sourceRect.X = 0; sourceRect.Y = 0; // Get the address of the data in the array by pinning it. GCHandle arrayHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned); try { IntPtr buffer = arrayHandle.AddrOfPinnedObject(); checked { buffer = new IntPtr(((long) buffer) + (long) offsetInBytes); sourceBufferSize -= offsetInBytes; } WritePixelsImpl(sourceRect, buffer, sourceBufferSize, stride, destinationX, destinationY, /*backwardsCompat*/ true); } finally { arrayHandle.Free(); } } } #endregion // Public Methods #region Protected Methods ////// Implementation of Freezable.CreateInstanceCore. /// ///The new Freezable. protected override Freezable CreateInstanceCore() { return new WriteableBitmap(); } ////// Implementation of Freezable.CloneCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces clones of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void CloneCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap) sourceFreezable; base.CloneCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.FreezeCore. /// ////// Critical: Accesses critical code. (AcquireBackBuffer, FreezeBackBuffer) /// TreatAsSafe: Method gets the back buffer, but doesn't expose it. /// [SecurityCritical, SecurityTreatAsSafe] protected override bool FreezeCore(bool isChecking) { bool canFreeze = (_lockCount == 0) && base.FreezeCore(isChecking); if (canFreeze && !isChecking) { Debug.Assert(_pBackBufferLock == null); // // By entering 'frozen' mode, we convert from being a // DoubleBufferedBitmap to a regular BitmapSource. // // Protect the back buffer for writing HRESULT.Check(MILSwDoubleBufferedBitmap.ProtectBackBuffer(_pDoubleBufferedBitmap)); // Get the back buffer to be used as our WicSourceHandle AcquireBackBuffer(TimeSpan.Zero, false); _needsUpdate = true; _hasDirtyRects = false; // From here on out we're going to effectively be an ordinary // BitmapSource. _actLikeSimpleBitmap = true; // Pull this resource off all the channels and put it back on. int channelCount = _duceResource.GetChannelCount(); for (int i = 0; i < channelCount; i++) { DUCE.IResource resource = this as DUCE.IResource; DUCE.Channel channel = _duceResource.GetChannel(i); // // It could have been added multiple times, so release until // it's no longer on a channel. // uint refCount = _duceResource.GetRefCountOnChannel(channel); for (uint j = 0; j < refCount; j++) { resource.ReleaseOnChannel(channel); } // Put it back on the Channel, only this time it wont // be a SwDoubleBufferedBitmap. for (uint j = 0; j < refCount; j++) { resource.AddRefOnChannel(channel); } } Debug.Assert(!_isWaitingForCommit); // We no longer need the SwDoubleBufferedBitmap _pDoubleBufferedBitmap.Dispose(); _pDoubleBufferedBitmap = null; // We will no longer need to wait for this event. _copyCompletedEvent.Close(); _copyCompletedEvent = null; // Clear out unused variables _committingBatchHandler = null; _pBackBuffer = null; } return canFreeze; } ////// Implementation of Freezable.CloneCurrentValueCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces clones of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void CloneCurrentValueCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap) sourceFreezable; base.CloneCurrentValueCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.GetAsFrozenCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces GetAsFrozen of original buffers. /// [SecurityCritical, SecurityTreatAsSafe] protected override void GetAsFrozenCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap)sourceFreezable; base.GetAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } ////// Implementation of Freezable.GetCurrentValueAsFrozenCore. /// ////// Critical: Accesses critical code. /// TreatAsSafe: Method only produces GetCurrentValueAsFrozen of original image. /// [SecurityCritical, SecurityTreatAsSafe] protected override void GetCurrentValueAsFrozenCore(Freezable sourceFreezable) { WriteableBitmap sourceBitmap = (WriteableBitmap)sourceFreezable; base.GetCurrentValueAsFrozenCore(sourceFreezable); CopyCommon(sourceBitmap); } #endregion // Protected Methods #region Private/Internal Methods ////// Initializes this WriteableBitmap with the /// contents of the specified BitmapSource. /// /// /// The BitmapSource to copy. /// ////// Critical: Creates and accesses a handle to an unmanaged resource. /// PublicOK: Inputs are safe. /// [SecurityCritical] private void InitFromBitmapSource( BitmapSource source ) { if (source == null) { throw new ArgumentNullException("source"); } if (source.PixelWidth < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } if (source.PixelHeight < 0) { // Backwards Compat HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } BeginInit(); _syncObject = source.SyncObject; lock (_syncObject) { Guid formatGuid = source.Format.Guid; SafeMILHandle internalPalette = new SafeMILHandle(); if (source.Format.Palettized) { internalPalette = source.Palette.InternalPalette; } HRESULT.Check(MILSwDoubleBufferedBitmap.Create( (uint)source.PixelWidth, // safe cast (uint)source.PixelHeight, // safe cast source.DpiX, source.DpiY, ref formatGuid, internalPalette, out _pDoubleBufferedBitmap )); Lock(); Int32Rect rcFull = new Int32Rect(0, 0, _pixelWidth, _pixelHeight); int bufferSize = checked(_backBufferStride.Value * source.PixelHeight); source.CriticalCopyPixels(rcFull, _backBuffer, bufferSize, _backBufferStride.Value); AddDirtyRect(rcFull); Unlock(); } EndInit(); } ////// Updates the pixels in the specified region of the bitmap. /// /// /// The rect to copy from the input buffer. /// /// /// The input buffer used to update the bitmap. /// /// /// The size of the input buffer in bytes. /// /// /// The stride of the input buffer in bytes. /// /// /// The destination x-coordinate of the left-most pixel to copy. /// /// /// The destination y-coordinate of the top-most pixel to copy. /// /// /// Whether or not to preserve the old WritePixels behavior. /// ////// Critical: Accesses critical code, performs unsafe operations. /// [SecurityCritical] private void WritePixelsImpl( Int32Rect sourceRect, IntPtr sourceBuffer, int sourceBufferSize, int sourceBufferStride, int destinationX, int destinationY, bool backwardsCompat ) { // // Sanitize the source rect and assure it will fit within the back buffer. // if (sourceRect.X < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (sourceRect.Y < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } if (sourceRect.Width < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } if (sourceRect.Width > _pixelWidth) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth)); } } if (sourceRect.Height < 0) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } if (sourceRect.Height > _pixelHeight) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight)); } } if (destinationX < 0) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } else { throw new ArgumentOutOfRangeException("sourceRect", SR.Get(SRID.ParameterCannotBeNegative)); } } if (destinationX > _pixelWidth - sourceRect.Width) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("destinationX", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelWidth - sourceRect.Width)); } } if (destinationY < 0) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_VALUEOVERFLOW); } else { throw new ArgumentOutOfRangeException("destinationY", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight - sourceRect.Height)); } } if (destinationY > _pixelHeight - sourceRect.Height) { if (backwardsCompat) { HRESULT.Check(MS.Win32.NativeMethods.E_INVALIDARG); } else { throw new ArgumentOutOfRangeException("destinationY", SR.Get(SRID.ParameterMustBeBetween, 0, _pixelHeight - sourceRect.Height)); } } // // Sanitize the other parameters. // if (sourceBuffer == IntPtr.Zero) { // Backwards Compat: // // The original code would null-ref when it was passed a null // buffer (IntPtr.Zero). We choose to throw a better // exception. throw new ArgumentNullException(backwardsCompat ? "buffer" : "sourceBuffer"); } if (sourceBufferStride < 1) { Debug.Assert(!backwardsCompat); throw new ArgumentOutOfRangeException("sourceBufferStride", SR.Get(SRID.ParameterCannotBeLessThan, 1)); } if (sourceRect.Width == 0 || sourceRect.Height == 0) { Debug.Assert(!backwardsCompat); // Nothing to do. return; } checked { uint finalRowWidthInBits = (uint)((sourceRect.X + sourceRect.Width) * _format.InternalBitsPerPixel); uint finalRowWidthInBytes = ((finalRowWidthInBits + 7) / 8); uint requiredBufferSize = (uint)((sourceRect.Y + sourceRect.Height - 1) * sourceBufferStride) + finalRowWidthInBytes; if (sourceBufferSize < requiredBufferSize) { if (backwardsCompat) { HRESULT.Check((int)WinCodecErrors.WINCODEC_ERR_INSUFFICIENTBUFFER); } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBufferSize), "sourceBufferSize"); } } uint copyWidthInBits = (uint)(sourceRect.Width * _format.InternalBitsPerPixel); // Calculate some offsets that we'll need in a moment. uint sourceXbyteOffset = (uint)((sourceRect.X * _format.InternalBitsPerPixel) / 8); uint sourceBufferBitOffset = (uint)((sourceRect.X * _format.InternalBitsPerPixel) % 8); uint firstPixelByteOffet = (uint)((sourceRect.Y * sourceBufferStride) + sourceXbyteOffset); uint destXbyteOffset = (uint)((destinationX * _format.InternalBitsPerPixel) / 8); uint destBufferBitOffset = (uint)((destinationX * _format.InternalBitsPerPixel) % 8); Int32Rect destinationRect = sourceRect; destinationRect.X = destinationX; destinationRect.Y = destinationY; // // Copy pixel information from the user supplied buffer to the back buffer. // unsafe { uint destOffset = (uint)(destinationY * _backBufferStride.Value) + destXbyteOffset; byte* pDest = (byte*)_backBuffer.ToPointer(); pDest += destOffset; uint outputBufferSize = _backBufferSize - destOffset; byte* pSource = (byte*)sourceBuffer.ToPointer(); pSource += firstPixelByteOffet; uint inputBufferSize = (uint)sourceBufferSize - firstPixelByteOffet; Lock(); MILUtilities.MILCopyPixelBuffer( pDest, outputBufferSize, (uint) _backBufferStride.Value, destBufferBitOffset, pSource, inputBufferSize, (uint) sourceBufferStride, sourceBufferBitOffset, (uint) sourceRect.Height, copyWidthInBits); AddDirtyRect(destinationRect); Unlock(); } } // Note: we do not call WritePostscript because we do not want to // raise change notifications until the writeable bitmap is unlocked. // // Change notifications may have already been raised in the Unlock // call in this method. } ////// Try to acquire the back buffer of our unmanaged double buffered bitmap in the specified timeout. /// /// /// The time to wait while trying to acquire the lock. /// /// /// Should we try to wait for the copy completed event? /// ///Returns true if the back buffer was acquired before the timeout expired. ////// Critical: Accesses a handle to an unmanaged resource. /// [SecurityCritical] private bool AcquireBackBuffer(TimeSpan timeout, bool waitForCopy) { bool backBufferAcquired = false; // // Only get the back buffer from the unmanaged double buffered bitmap if this is our // first time being called since the last successful call to OnCommittingBatch. // OnCommittingBatch sets _pBackBuffer to null. // if (_pBackBuffer == null) { bool shouldGetBackBuffer = true; if (waitForCopy) { // If we have committed a copy-forward command, we need to wait // for the render thread to finish the copy before we can use // the back buffer. shouldGetBackBuffer = _copyCompletedEvent.WaitOne(timeout, false); } if (shouldGetBackBuffer) { MILSwDoubleBufferedBitmap.GetBackBuffer( _pDoubleBufferedBitmap, out _pBackBuffer, out _backBufferSize); _syncObject = WicSourceHandle = _pBackBuffer; backBufferAcquired = true; } } else { backBufferAcquired = true; } return backBufferAcquired; } ////// Common implementation for CloneCore(), CloneCurrentValueCore(), /// GetAsFrozenCore(), and GetCurrentValueAsFrozenCore(). /// /// The WriteableBitmap to copy from. ////// Critical: Accesses a handle to an unmanaged resource. /// [SecurityCritical] private void CopyCommon(WriteableBitmap sourceBitmap) { // Avoid Animatable requesting resource updates for invalidations // that occur during construction. Animatable_IsResourceInvalidationNecessary = false; _actLikeSimpleBitmap = false; // Create a SwDoubleBufferedBitmap and copy the sourceBitmap into it. InitFromBitmapSource(sourceBitmap); // The next invalidation will cause Animatable to register an // UpdateResource callback. Animatable_IsResourceInvalidationNecessary = true; } // ISupportInitialize ////// Prepare the bitmap to accept initialize paramters. /// private void BeginInit() { _bitmapInit.BeginInit(); } ////// Prepare the bitmap to accept initialize paramters. /// ////// Critical: Access critical resources. /// [SecurityCritical, SecurityTreatAsSafe] private void EndInit() { _bitmapInit.EndInit(); FinalizeCreation(); } ////// Create the unmanaged resources. /// ////// Critical - access critical resource /// [SecurityCritical] internal override void FinalizeCreation() { IsSourceCached = true; CreationCompleted = true; UpdateCachedSettings(); } ////// Get the size of the specified array and of the elements in it. /// /// /// The array to get info about. /// /// /// On output, will contain the size of the elements in the array. /// /// /// On output, will contain the size of the array. /// ////// Critical - Calls Marshal.SizeOf, which has a link demand for some reason. /// [SecurityCritical] private void ValidateArrayAndGetInfo(Array sourceBuffer, bool backwardsCompat, out int elementSize, out int sourceBufferSize, out Type elementType) { // // Assure that a valid pixels Array was provided. // if (sourceBuffer == null) { throw new ArgumentNullException(backwardsCompat ? "pixels" : "sourceBuffer"); } if (sourceBuffer.Rank == 1) { if (sourceBuffer.GetLength(0) <= 0) { if (backwardsCompat) { elementSize = 1; sourceBufferSize = 0; elementType = null; } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBuffer), "sourceBuffer"); } } else { checked { object exemplar = sourceBuffer.GetValue(0); elementSize = Marshal.SizeOf(exemplar); sourceBufferSize = sourceBuffer.GetLength(0) * elementSize; elementType = exemplar.GetType(); } } } else if (sourceBuffer.Rank == 2) { if (sourceBuffer.GetLength(0) <= 0 || sourceBuffer.GetLength(1) <= 0) { if (backwardsCompat) { elementSize = 1; sourceBufferSize = 0; elementType = null; } else { throw new ArgumentException(SR.Get(SRID.Image_InsufficientBuffer), "sourceBuffer"); } } else { checked { object exemplar = sourceBuffer.GetValue(0,0); elementSize = Marshal.SizeOf(exemplar); sourceBufferSize = sourceBuffer.GetLength(0) * sourceBuffer.GetLength(1) * elementSize; elementType = exemplar.GetType(); } } } else { throw new ArgumentException(SR.Get(SRID.Collection_BadRank), backwardsCompat ? "pixels" : "sourceBuffer"); } } ////// Adds a reference to our DUCE resource on /// /// The channel we want to AddRef on. /// ///. /// /// The handle to our DoubleBufferedBitmap or BitmapSource handle. /// ////// We override this method because we use a different resource /// type than our base class does. This probably suggests that the /// base class should not presume the resource type, but it /// currently does. The base class uses TYPE_BITMAPSOURCE /// resources, and we use TYPE_DOUBLEBUFFEREDBITMAP resources. /// internal override DUCE.ResourceHandle AddRefOnChannelCore(DUCE.Channel channel) { // // If we're in BitmapSource mode, then just defer to the BitmapSource // implementation. // if (_actLikeSimpleBitmap) { return base.AddRefOnChannelCore(channel); } if (_duceResource.CreateOrAddRefOnChannel(channel, DUCE.ResourceType.TYPE_DOUBLEBUFFEREDBITMAP)) { // This is the first AddRef on this channel... // If we are being put onto the asynchronous compositor channel in // a dirty state, we need to subscribe to the CommittingBatch event. if (!channel.IsSynchronous && _hasDirtyRects) { SubscribeToCommittingBatch(); } AddRefOnChannelAnimations(channel); // The first time our resource is created on a channel, we need // to update it. We can skip "on channel" check since we // already know that the resource is on channel. UpdateResource(channel, true); } return _duceResource.GetHandle(channel); } internal override void ReleaseOnChannelCore(DUCE.Channel channel) { Debug.Assert(_duceResource.IsOnChannel(channel)); if (_duceResource.ReleaseOnChannel(channel)) { // This is the last release from this channel... // If we are being pulled off the asynchronous compositor channel // then unsubscribe from the CommittingBatch event. if (!channel.IsSynchronous) { UnsubscribeFromCommittingBatch(); } ReleaseOnChannelAnimations(channel); } } ////// Updates the double-buffered bitmap DUCE resource with a pointer to our acutal object. /// /// The channel to update the resource on. /// /// If this is true, we know we are on channel and don't need to explicitly check. /// ////// Critical: Accesses a handle to an unmanaged resource. Calls critical methods. /// TreateAsSafe: We allocate the double buffered bitmap ourself and the user can never /// get ahold of it. /// [SecurityCritical, SecurityTreatAsSafe] internal override void UpdateBitmapSourceResource(DUCE.Channel channel, bool skipOnChannelCheck) { // // If we're in BitmapSource mode, then just defer to the BitmapSource // implementation. // if (_actLikeSimpleBitmap) { base.UpdateBitmapSourceResource(channel, skipOnChannelCheck); return; } // We override this method because we use a different resource type // than our base class does. This probably suggests that the base // class should not presume the resource type, but it currently // does. The base class uses TYPE_BITMAPSOURCE resources, and we // use TYPE_DOUBLEBUFFEREDBITMAP resources. // 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)) { DUCE.MILCMD_DOUBLEBUFFEREDBITMAP command; command.Type = MILCMD.MilCmdDoubleBufferedBitmap; command.Handle = _duceResource.GetHandle(channel); unsafe { command.SwDoubleBufferedBitmap = (UInt64) _pDoubleBufferedBitmap.DangerousGetHandle().ToPointer(); } command.UseBackBuffer = channel.IsSynchronous ? 1u : 0u; // // We need to ensure that this object stays alive while traveling over the channel // so we'll AddRef it here, and simply take over the reference on the other side. // UnsafeNativeMethods.MILUnknown.AddRef(_pDoubleBufferedBitmap); unsafe { channel.SendSecurityCriticalCommand( (byte*)&command, sizeof(DUCE.MILCMD_DOUBLEBUFFEREDBITMAP) ); } } } private void SubscribeToCommittingBatch() { // Only subscribe the the CommittingBatch event if we are on-channel. if (!_isWaitingForCommit) { MediaContext mediaContext = MediaContext.From(Dispatcher); if (_duceResource.IsOnChannel(mediaContext.Channel)) { mediaContext.CommittingBatch += CommittingBatchHandler; _isWaitingForCommit = true; } } } private void UnsubscribeFromCommittingBatch() { if (_isWaitingForCommit) { MediaContext mediaContext = MediaContext.From(Dispatcher); mediaContext.CommittingBatch -= CommittingBatchHandler; _isWaitingForCommit = false; } } ////// Send a packet on the DUCE.Channel telling our double-buffered bitmap resource /// to copy forward dirty regions from the back buffer to the front buffer. /// ////// For the packet to be sent, the user must have added a dirty region to the /// WriteableBitmap and there must be no outstanding locks. /// ////// Critical: Accesses critical methods and a handle to an unmanaged resource. /// TreatAsSafe: Inputs are supplied internally. /// [SecurityCritical, SecurityTreatAsSafe] private void OnCommittingBatch(object sender, EventArgs args) { Debug.Assert(_isWaitingForCommit); // How else are we here? UnsubscribeFromCommittingBatch(); Debug.Assert(_lockCount == 0); // How else are we here? Debug.Assert(_hasDirtyRects); // How else are we here? // Before using the back buffer again, we need to know when // the rendering thread has completed the copy. By setting // our back buffer pointer to null, we'll have to re-acquire // it the next time, which will wait for the copy to complete. _copyCompletedEvent.Reset(); _pBackBuffer = null; DUCE.Channel channel = sender as DUCE.Channel; Debug.Assert(_duceResource.IsOnChannel(channel)); // How else are we here? // We are going to pass an event in the command packet we send to // the composition thread. We need to make sure the event stays // alive in case we get collected before the composition thread // processes the packet. We do this by duplicating the event // handle, and the composition thread will close the handle after // signalling it. IntPtr hDuplicate; IntPtr hCurrentProc = MS.Win32.UnsafeNativeMethods.GetCurrentProcess(); if (!MS.Win32.UnsafeNativeMethods.DuplicateHandle( hCurrentProc, _copyCompletedEvent.SafeWaitHandle, hCurrentProc, out hDuplicate, 0, false, MS.Win32.UnsafeNativeMethods.DUPLICATE_SAME_ACCESS )) { throw new Win32Exception(); } DUCE.MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD command; command.Type = MILCMD.MilCmdDoubleBufferedBitmapCopyForward; command.Handle = _duceResource.GetHandle(channel); command.CopyCompletedEvent = (UInt64) hDuplicate.ToInt64(); unsafe { channel.SendSecurityCriticalCommand( (byte*)&command, sizeof(DUCE.MILCMD_DOUBLEBUFFEREDBITMAP_COPYFORWARD) ); } // We are committing the batch to the asynchronous compositor, // which will copy the rects forward. The copy will complete // before we can access the back buffer again. So, we consider // ourselves clean. _hasDirtyRects = false; } #endregion #region Properties ////// Read-only data pointer to the back buffer. /// ////// Critical: Accesses back buffer pointer. /// PublicOK: Demands unmanaged code permission. /// public IntPtr BackBuffer { [SecurityCritical] get { SecurityHelper.DemandUnmanagedCode(); ReadPreamble(); return _backBuffer; } [SecurityCritical] private set { _backBuffer = value; } } ////// Critical: The pointer to the back buffer pixels. /// [SecurityCritical] private IntPtr _backBuffer; ////// Critical: The size of the back buffer. Should never be exposed /// to external parties. /// [SecurityCritical] private uint _backBufferSize; ////// Read-only stride of the back buffer. /// public int BackBufferStride { get { ReadPreamble(); return _backBufferStride.Value; } } ////// Critical: Sets the back buffer stride, which is important for /// internal pointer arithmetic calculations. /// private SecurityCriticalDataForSet_backBufferStride; #endregion // Properties #region Fields /// /// Critical: The pointer to the IMILSwDoubleBufferedBitmap /// interface that manages the back and front buffers. /// [SecurityCritical] private SafeMILHandle _pDoubleBufferedBitmap; // CSwDoubleBufferedBitmap ////// Critical: The pointer to the IWICBitmapLock interface that /// provides access to the back buffer pointer. /// [SecurityCritical] private SafeMILHandle _pBackBufferLock; // IWICBitmapLock ////// Critical: The pointer to the IMILBitmap interface that is the /// back buffer. /// [SecurityCritical] private BitmapSourceSafeMILHandle _pBackBuffer; // IMILBitmap private uint _lockCount = 0; // Flags whether the user has added a dirty rect since the last CopyForward packet was sent. private bool _hasDirtyRects = true; // Flags whether a MediaContext.CommittingBatch handler has already been added. private bool _isWaitingForCommit = false; private ManualResetEvent _copyCompletedEvent = new ManualResetEvent(true); private EventHandler CommittingBatchHandler { get { if (_committingBatchHandler == null) { _committingBatchHandler = OnCommittingBatch; } return _committingBatchHandler; } } private EventHandler _committingBatchHandler; // = OnCommittingBatch (CS0236) private bool _actLikeSimpleBitmap = false; #endregion // Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FixedPageAutomationPeer.cs
- ListCommandEventArgs.cs
- EntityContainer.cs
- XmlResolver.cs
- RotateTransform.cs
- XPathDocumentNavigator.cs
- EntityObject.cs
- DataSourceView.cs
- ZoneMembershipCondition.cs
- DoubleUtil.cs
- WrapPanel.cs
- SystemDiagnosticsSection.cs
- BaseCollection.cs
- ColumnMapCopier.cs
- AsyncStreamReader.cs
- RadioButtonPopupAdapter.cs
- AttributeProviderAttribute.cs
- SqlErrorCollection.cs
- ConnectivityStatus.cs
- RoleManagerSection.cs
- SemanticKeyElement.cs
- PointLight.cs
- XmlAnyElementAttribute.cs
- ErrorEventArgs.cs
- DefaultSettingsSection.cs
- rsa.cs
- AsyncContentLoadedEventArgs.cs
- UserInitiatedNavigationPermission.cs
- DataViewManagerListItemTypeDescriptor.cs
- FormViewRow.cs
- TextProperties.cs
- ToolStripProgressBar.cs
- SqlReferenceCollection.cs
- BitmapEffectInput.cs
- ValidationErrorEventArgs.cs
- DocumentApplication.cs
- _ConnectOverlappedAsyncResult.cs
- GeneralTransform.cs
- SafeNativeMethods.cs
- BoundField.cs
- TransactionScope.cs
- FlowDocumentFormatter.cs
- SupportingTokenSecurityTokenResolver.cs
- SessionParameter.cs
- HtmlInputButton.cs
- BrushValueSerializer.cs
- DebugHandleTracker.cs
- ExpressionBuilder.cs
- FormatterServices.cs
- CustomWebEventKey.cs
- SocketPermission.cs
- WebPartConnectionsCancelEventArgs.cs
- AxHost.cs
- Adorner.cs
- EventWaitHandle.cs
- XsdCachingReader.cs
- LayoutDump.cs
- XPathDocumentNavigator.cs
- SharedStream.cs
- SafeArrayRankMismatchException.cs
- QueryableDataSource.cs
- SqlDataSourceFilteringEventArgs.cs
- PropertyGridEditorPart.cs
- ToolStripHighContrastRenderer.cs
- RouteTable.cs
- MarshalByRefObject.cs
- ProfileGroupSettings.cs
- RequestCacheValidator.cs
- FormatterConverter.cs
- MimeAnyImporter.cs
- DataSetMappper.cs
- _TLSstream.cs
- wmiprovider.cs
- DispatcherEventArgs.cs
- AppDomainProtocolHandler.cs
- SqlConnection.cs
- HttpClientCertificate.cs
- HtmlTableRowCollection.cs
- InstanceData.cs
- BypassElementCollection.cs
- DbParameterHelper.cs
- DataGrid.cs
- RequestContext.cs
- AutomationPropertyInfo.cs
- Line.cs
- RoutedCommand.cs
- IgnoreFlushAndCloseStream.cs
- StylusShape.cs
- SqlRowUpdatedEvent.cs
- DigestComparer.cs
- WebPartMenuStyle.cs
- AppDomainProtocolHandler.cs
- BridgeDataReader.cs
- EntityModelSchemaGenerator.cs
- LinkClickEvent.cs
- URLBuilder.cs
- UniqueID.cs
- HelpEvent.cs
- SizeAnimationUsingKeyFrames.cs
- InfiniteIntConverter.cs