|
//------------------------------------------------------------------------------
// 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
/// <summary>
/// Interface for Bitmap Sources, included decoders and effects
/// </summary>
[Localizability(LocalizationCategory.None, Readability = Readability.Unreadable)]
public abstract class BitmapSource : ImageSource, DUCE.IResource
{
#region Constructor
/// <summary>
/// Create a BitmapSource from an array of pixels.
/// </summary>
/// <param name="pixelWidth">Width of the Bitmap</param>
/// <param name="pixelHeight">Height of the Bitmap</param>
/// <param name="dpiX">Horizontal DPI of the Bitmap</param>
/// <param name="dpiY">Vertical DPI of the Bitmap</param>
/// <param name="pixelFormat">Format of the Bitmap</param>
/// <param name="palette">Palette of the Bitmap</param>
/// <param name="pixels">Array of pixels</param>
/// <param name="stride">stride</param>
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);
}
/// <summary>
/// Create a BitmapSource from an array of pixels in unmanaged memory.
/// </summary>
/// <param name="pixelWidth">Width of the Bitmap</param>
/// <param name="pixelHeight">Height of the Bitmap</param>
/// <param name="dpiX">Horizontal DPI of the Bitmap</param>
/// <param name="dpiY">Vertical DPI of the Bitmap</param>
/// <param name="pixelFormat">Format of the Bitmap</param>
/// <param name="palette">Palette of the Bitmap</param>
/// <param name="buffer">Pointer to the buffer in memory</param>
/// <param name="bufferSize">Size of the buffer</param>
/// <param name="stride">stride</param>
/// <remarks>
/// Callers must have UnmanagedCode permission to call this API.
/// </remarks>
/// <SecurityNote>
/// Critical - access critical code, accepts pointer arguments
/// PublicOK - demands unmanaged code permission
/// </SecurityNote>
[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);
}
/// <summary>
/// Constructor
/// </summary>
/// <SecurityNote>
/// Critical: Accesses _wicSource
/// TreatAsSafe: Inputs are safe
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
protected BitmapSource()
{
// Synchronize for *this* object only by default.
_syncObject = _bitmapInit;
_isSourceCached = false;
}
/// <summary>
/// Internal Constructor
///
/// useVirtuals: Should properties and methods like PixelWidth and CopyPixels use their "default" implementation.
/// </summary>
/// <SecurityNote>
/// Critical: Accesses _wicSource
/// TreatAsSafe: Input (bool) is safe
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal BitmapSource(bool useVirtuals)
{
_useVirtuals = true;
_isSourceCached = false;
// Synchronize for *this* object only by default.
_syncObject = _bitmapInit;
}
/// <summary>
/// Creates a copy of this object.
/// </summary>
/// <returns>The copy.</returns>
public new BitmapSource Clone()
{
return (BitmapSource)base.Clone();
}
/// <summary>
/// Shadows inherited CloneCurrentValue() with a strongly typed version for convenience.
/// </summary>
/// <returns>The copy.</returns>
public new BitmapSource CloneCurrentValue()
{
return (BitmapSource)base.CloneCurrentValue();
}
#endregion Constructor
#region Public properties and methods
/// <summary>
/// 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.
/// </summary>
public virtual System.Windows.Media.PixelFormat Format
{
get
{
ReadPreamble();
EnsureShouldUseVirtuals();
_bitmapInit.EnsureInitializedComplete();
CompleteDelayedCreation();
return _format;
}
}
/// <summary>
/// Width, in pixels, of the bitmap.
/// </summary>
public virtual int PixelWidth
{
get
{
ReadPreamble();
EnsureShouldUseVirtuals();
_bitmapInit.EnsureInitializedComplete();
CompleteDelayedCreation();
return _pixelWidth;
}
}
/// <summary>
/// Height, in pixels, of the bitmap.
/// </summary>
public virtual int PixelHeight
{
get
{
ReadPreamble();
EnsureShouldUseVirtuals();
_bitmapInit.EnsureInitializedComplete();
CompleteDelayedCreation();
return _pixelHeight;
}
}
/// <summary>
/// Horizontal DPI of the bitmap.
/// </summary>
public virtual double DpiX
{
get
{
ReadPreamble();
EnsureShouldUseVirtuals();
_bitmapInit.EnsureInitializedComplete();
CompleteDelayedCreation();
return _dpiX;
}
}
/// <summary>
/// Vertical DPI of the bitmap.
/// </summary>
public virtual double DpiY
{
get
{
ReadPreamble();
EnsureShouldUseVirtuals();
_bitmapInit.EnsureInitializedComplete();
CompleteDelayedCreation();
return _dpiY;
}
}
/// <summary>
/// Retrieve and set the bitmap palette.
/// </summary>
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;
}
}
/// <summary>
/// Returns true if the BitmapSource is downloading content
/// </summary>
public virtual bool IsDownloading
{
get
{
ReadPreamble();
return false;
}
}
/// <summary>
/// Raised when downloading content is done
/// May not be raised for all content.
/// </summary>
public virtual event EventHandler DownloadCompleted
{
add
{
WritePreamble();
_downloadEvent.AddEvent(value);
}
remove
{
WritePreamble();
_downloadEvent.RemoveEvent(value);
}
}
/// <summary>
/// Raised when download has progressed
/// May not be raised for all content.
/// </summary>
public virtual event EventHandler<DownloadProgressEventArgs> DownloadProgress
{
add
{
WritePreamble();
_progressEvent.AddEvent(value);
}
remove
{
WritePreamble();
_progressEvent.RemoveEvent(value);
}
}
/// <summary>
/// Raised when download has failed
/// May not be raised for all content.
/// </summary>
public virtual event EventHandler<ExceptionEventArgs> DownloadFailed
{
add
{
WritePreamble();
_failedEvent.AddEvent(value);
}
remove
{
WritePreamble();
_failedEvent.RemoveEvent(value);
}
}
/// <summary>
/// Raised when decoding has failed
/// May not be raised for all content.
/// </summary>
public virtual event EventHandler<ExceptionEventArgs> DecodeFailed
{
add
{
WritePreamble();
EnsureShouldUseVirtuals();
_decodeFailedEvent.AddEvent(value);
}
remove
{
WritePreamble();
EnsureShouldUseVirtuals();
_decodeFailedEvent.RemoveEvent(value);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sourceRect">Source rect to copy. Int32Rect.Empty specifies the entire rect</param>
/// <param name="pixels">Destination array</param>
/// <param name="stride">Stride</param>
/// <param name="offset">Offset in the array to begin copying</param>
/// <SecurityNote>
/// 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
/// </SecurityNote>
[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);
}
/// <summary>
/// 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).
/// </summary>
/// <param name="pixels">Destination array</param>
/// <param name="stride">Stride</param>
/// <param name="offset">Offset to begin at</param>
/// <SecurityNote>
/// 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
/// </SecurityNote>
[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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="sourceRect">Source rect to copy. Int32Rect.Empty specified entire Bitmap</param>
/// <param name="buffer">Pointer to the buffer</param>
/// <param name="bufferSize">Size of buffer</param>
/// <param name="stride">Stride</param>
/// <SecurityNote>
/// 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
/// </SecurityNote>
[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);
}
/// <summary>
/// Get the width of the bitmap in measure units (96ths of an inch).
/// </summary>
public override double Width
{
get
{
ReadPreamble();
return GetWidthInternal();
}
}
/// <summary>
/// Get the width of the bitmap in measure units (96ths of an inch).
/// </summary>
public override double Height
{
get
{
ReadPreamble();
return GetHeightInternal();
}
}
/// <summary>
/// Get the Metadata of the bitmap
/// </summary>
public override ImageMetadata Metadata
{
get
{
ReadPreamble();
return null;
}
}
#endregion
#region Internal, Protected and Private properties and methods
/// <summary>
/// Helper function to calculate Width.
/// </summary>
private double GetWidthInternal()
{
return ImageSource.PixelsToDIPs(this.DpiX, this.PixelWidth);
}
/// <summary>
/// Helper function to calculate Height.
/// </summary>
private double GetHeightInternal()
{
return ImageSource.PixelsToDIPs(this.DpiY, this.PixelHeight);
}
/// <summary>
/// Get the Size for the bitmap
/// </summary>
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;
}
}
/// <SecurityNote>
/// 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.
/// </SecurityNote>
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
///
/// <SecurityNote>
/// Critical - access/creates critical resources
/// </SecurityNote>
[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;
}
/// <summary>
/// CriticalCopyPixels
/// </summary>
/// <param name="sourceRect"></param>
/// <param name="pixels"></param>
/// <param name="stride"></param>
/// <param name="offset"></param>
/// <SecurityNote>
/// Critical: This code can be used to expose pixel information
/// </SecurityNote>
[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);
}
}
/// <summary>
/// CriticalCopyPixels
/// </summary>
/// <param name="sourceRect"></param>
/// <param name="buffer"></param>
/// <param name="bufferSize"></param>
/// <param name="stride"></param>
/// <SecurityNote>
/// Critical: There is a risk of buffer overruns when copying data to the buffer.
/// This code can also be used to expose pixel information
/// </SecurityNote>
[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
));
}
}
/// <SecurityNote>
/// Critical: This code is used to check and grant access to pixel data
/// TreatAsSafe: This code does not elevate
/// </SecurityNote>
[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);
}
/// <summary>
/// Called when DUCE resource requires updating
/// </summary>
/// <SecurityNote>
/// Critical - access critical resource _convertedDUCEPtr
/// TreatAsSafe - All inputs verified
/// </SecurityNote>
[SecurityCritical, SecurityTreatAsSafe]
internal override void UpdateResource(DUCE.Channel channel, bool skipOnChannelCheck)
{
base.UpdateResource(channel, skipOnChannelCheck);
UpdateBitmapSourceResource(channel, skipOnChannelCheck);
}
/// <SecurityNote>
/// Critical - access/create critical resource, returns unmanaged pointer
/// </SecurityNote>
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
{
if (UsableWithoutCache)
{
#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 (!HasCompatibleFormat)
{
#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);
}
/// <SecurityNote>
/// SecurityCritical: This code calls critical code SendCommandBitmapSource
/// SecurityTreatAsSafe: The code called into demands
/// </SecurityNote>
[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 sync lock here.
lock (_syncObject)
{
channel.SendCommandBitmapSource(
_duceResource.GetHandle(channel),
DUCECompatiblePtr
);
}
}
}
/// <summary>
/// Called when a failure to decode is detected.
/// </summary>
/// <SecurityNote>
/// Critical - access critical resource WicSourceHandle
/// TreatAsSafe - All inputs verified
/// </SecurityNote>
[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
/// <SecurityNote>
/// Critical - access critical resource
/// </SecurityNote>
[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 (!IsCompatibleFormat(originalFmt))
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
/// <summary>
/// Called by the base Freezable class to make this object
/// frozen.
/// </summary>
protected override bool FreezeCore(bool isChecking)
{
if (!base.FreezeCore(isChecking))
{
return false;
}
if (IsDownloading)
{
return false;
}
return true;
}
/// <summary>
/// Copy the fields not covered by DPs. This is used by
/// CloneCore(), CloneCurrentValueCore(), GetAsFrozenCore() and
/// GetCurrentValueAsFrozenCore().
/// </summary>
/// <SecurityNote>
/// SecurityCritical: This code accesses unmanaged resources
/// SecurityTreatAsSafe: Inputs are verified
/// </SecurityNote>
[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);
}
}
/// <summary>
/// Implementation of <see cref="System.Windows.Freezable.CloneCore(Freezable)">Freezable.CloneCore</see>.
/// </summary>
protected override void CloneCore(Freezable sourceFreezable)
{
BitmapSource sourceBitmap = (BitmapSource)sourceFreezable;
base.CloneCore(sourceFreezable);
CopyCommon(sourceBitmap);
}
/// <summary>
/// Implementation of <see cref="System.Windows.Freezable.CloneCurrentValueCore(Freezable)">Freezable.CloneCurrentValueCore</see>.
/// </summary>
protected override void CloneCurrentValueCore(Freezable sourceFreezable)
{
BitmapSource sourceBitmap = (BitmapSource)sourceFreezable;
base.CloneCurrentValueCore(sourceFreezable);
CopyCommon(sourceBitmap);
}
/// <summary>
/// Implementation of <see cref="System.Windows.Freezable.GetAsFrozenCore(Freezable)">Freezable.GetAsFrozenCore</see>.
/// </summary>
protected override void GetAsFrozenCore(Freezable sourceFreezable)
{
BitmapSource sourceBitmap = (BitmapSource)sourceFreezable;
base.GetAsFrozenCore(sourceFreezable);
CopyCommon(sourceBitmap);
}
/// <summary>
/// Implementation of <see cref="System.Windows.Freezable.GetCurrentValueAsFrozenCore(Freezable)">Freezable.GetCurrentValueAsFrozenCore</see>.
/// </summary>
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();
/// <SecurityNote>
/// Critical: Unmanaged IWICBitmapSource handle. Access only through WicSourceHandle
/// </SecurityNote>
[SecurityCritical]
internal BitmapSourceSafeMILHandle _wicSource = null;
/// <SecurityNote>
/// Critical: Unmanaged IWICBitmapSource handle.
/// </SecurityNote>
[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<DownloadProgressEventArgs> _progressEvent = new UniqueEventHelper<DownloadProgressEventArgs>();
internal UniqueEventHelper<ExceptionEventArgs> _failedEvent = new UniqueEventHelper<ExceptionEventArgs>();
internal UniqueEventHelper<ExceptionEventArgs> _decodeFailedEvent = new UniqueEventHelper<ExceptionEventArgs>();
// 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();
// Whether or not the _wicSource handle must be cached on the UI Thread
// before being passed to the render thread.
internal bool UsableWithoutCache
{
get
{
return HasCompatibleFormat && _isSourceCached;
}
}
// Whether or not the _wicSource has a pixel format that is compatible
// with the render thread.
internal bool HasCompatibleFormat
{
get
{
return IsCompatibleFormat(Format);
}
}
// Whether or not the specified format is compatible with the render
// thread.
internal static bool IsCompatibleFormat(PixelFormat format)
{
return (Array.IndexOf(s_supportedDUCEFormats, format) != -1);
}
/// 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
{
// When someone derives from BitmapSource, we create an instance
// of the ManagedBitmapSource wrapping the instance to implement
// the actual IWicBitmapSource interface for MIL to use. We use
// COM interop (Marshal.GetComInterfaceForObject) to cast the
// ManagedBitmapSource to the COM interface. This pins the
// ManagedBitmapSource behind the reference-counted COM interface,
// which is then stored in a BitmapSourceSafeMILHandle, which is
// held by the original BitmapSource instance. The pinned
// ManagedBitmapSource also holds a reference to the orginal
// BitmapSource, thus a cycle is created that cannot be broken by
// the GC.
//
// It looks like this:
//
// CustomBitmapSource -->
// BitmapSourceSafeMILHandle (ref counted) -->
// ManagedBitmapSource -->
// CustomBitmapSource
//
// To avoid this cycle, we hold a weak reference to the original
// BitmapSource.
private WeakReference<BitmapSource> _bitmapSource;
public ManagedBitmapSource(BitmapSource bitmapSource)
{
if (bitmapSource == null)
{
throw new System.ArgumentNullException("bitmapSource");
}
_bitmapSource = new WeakReference<BitmapSource>(bitmapSource);
}
int IWICBitmapSource.GetSize(out int puiWidth, out int puiHeight)
{
BitmapSource bitmapSource;
if(_bitmapSource.TryGetTarget(out bitmapSource))
{
puiWidth = bitmapSource.PixelWidth;
puiHeight = bitmapSource.PixelHeight;
return NativeMethods.S_OK;
}
else
{
puiWidth = 0;
puiHeight = 0;
return NativeMethods.E_FAIL;
}
}
int IWICBitmapSource.GetPixelFormat(out Guid guidFormat)
{
BitmapSource bitmapSource;
if(_bitmapSource.TryGetTarget(out bitmapSource))
{
guidFormat = bitmapSource.Format.Guid;
return NativeMethods.S_OK;
}
else
{
guidFormat = Guid.Empty;
return NativeMethods.E_FAIL;
}
}
int IWICBitmapSource.GetResolution(out double pDpiX, out double pDpiY)
{
BitmapSource bitmapSource;
if(_bitmapSource.TryGetTarget(out bitmapSource))
{
pDpiX = bitmapSource.DpiX;
pDpiY = bitmapSource.DpiY;
return NativeMethods.S_OK;
}
else
{
pDpiX = 0.0;
pDpiY = 0.0;
return NativeMethods.E_FAIL;
}
}
/// <SecurityNote>
/// Critical - Calls into a P/Invoke method InitializeFromPalette().
/// </SecurityNote>
[SecurityCritical]
int IWICBitmapSource.GetPalette(IntPtr /* IWICPalette */ pIPalette)
{
BitmapSource bitmapSource;
if(_bitmapSource.TryGetTarget(out bitmapSource))
{
BitmapPalette palette = bitmapSource.Palette;
if ((palette == null) || (palette.InternalPalette == null) || palette.InternalPalette.IsInvalid)
{
return (int)WinCodecErrors.WINCODEC_ERR_PALETTEUNAVAILABLE;
}
HRESULT.Check(UnsafeNativeMethods.WICPalette.InitializeFromPalette(pIPalette, palette.InternalPalette));
return NativeMethods.S_OK;
}
else
{
return NativeMethods.E_FAIL;
}
}
/// <SecurityNote>
/// Critical: This code can be used to expose pixel information
/// </SecurityNote>
[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;
}
BitmapSource bitmapSource;
if(_bitmapSource.TryGetTarget(out bitmapSource))
{
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;
}
else
{
return NativeMethods.E_FAIL;
}
}
}
#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
}
|