Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Core / CSharp / System / Windows / Media / ColorContext.cs / 1305600 / ColorContext.cs
//------------------------------------------------------------------------------ // Microsoft Windows Client Platform // Copyright (c) Microsoft Corporation, All Rights Reserved. // // File: ColorContext.cs //----------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages using System; using MS.Internal; using MS.Internal.PresentationCore; using MS.Win32; using System.IO; using System.Runtime.InteropServices; using System.Windows.Media; using System.Resources; using System.Security; using System.Security.Permissions; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using System.Diagnostics; using System.Globalization; using Microsoft.Win32.SafeHandles; using System.Net; using System.IO.Packaging; using System.Windows.Navigation; using System.Diagnostics.CodeAnalysis; using System.Text; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; using UnsafeNativeMethodsMilCoreApi = MS.Win32.PresentationCore.UnsafeNativeMethods; using IWICCC = MS.Win32.PresentationCore.UnsafeNativeMethods.IWICColorContext; namespace System.Windows.Media { ////// Color Context /// public class ColorContext { #region Constructors ////// Create a ColorContext from an unmanaged color context /// ////// SecurityCritical: This code sets a critical data member, calls critical code /// [SecurityCritical] private ColorContext(SafeMILHandle colorContextHandle) { _colorContextHandle = colorContextHandle; // // For 3.* backwards compat, we aren't going to HRESULT.Check() anywhere because // that could introduce new exceptions. If anything fails, _colorContextHelper // will be invalid and we'll emulate the old failure behavior later in // OpenProfileStream() // IWICCC.WICColorContextType type; if (HRESULT.Failed(IWICCC.GetType(_colorContextHandle, out type))) { return; } switch (type) { case IWICCC.WICColorContextType.WICColorContextProfile: uint cbProfileActual; int hr = IWICCC.GetProfileBytes(_colorContextHandle, 0, null, out cbProfileActual); if (HRESULT.Succeeded(hr) && cbProfileActual != 0) { byte[] profileData = new byte[cbProfileActual]; if (HRESULT.Failed(IWICCC.GetProfileBytes( _colorContextHandle, cbProfileActual, profileData, out cbProfileActual)) ) { return; } FromRawBytes(profileData, (int)cbProfileActual, /* dontThrowException = */ true); } break; case IWICCC.WICColorContextType.WICColorContextExifColorSpace: uint colorSpace; if (HRESULT.Failed(IWICCC.GetExifColorSpace(_colorContextHandle, out colorSpace))) { return; } // // From MSDN: // "1" is sRGB. We will use our built-in sRGB profile. // "2" is Adobe RGB. WIC says we should never see this because they are nonstandard and instead a // real profile will be returned. // "3-65534" is unused. // // From the Exif spec: // B. Tag Relating to Color Space // ColorSpace // // The color space information tag (ColorSpace) is always recorded as the color space specifier. // Normally sRGB (=1) is used to define the color space based on the PC monitor conditions and environment. If a // color space other than sRGB is used, Uncalibrated (=FFFF.H) is set. Image data recorded as Uncalibrated can be // treated as sRGB when it is converted to Flashpix. On sRGB see Annex E. // Tag = 40961 (A001.H) // Type = SHORT // Count = 1 // 1 = sRGB // FFFF.H = Uncalibrated // // So for 65535 we will return sRGB since it is acceptible rather than having an invalid ColorContext. The Exif // CC should always be the second one so the real one is given priority. Alternatively, we could ignore the // uncalibrated CC but that would be a breaking change with 3.* (returning 1 instead of 2). // // If anything other than 1 or 65535 happens, _colorContextHelper will remain invalid and we will emulate // the old crash behavior in OpenProfileStream(). // if (colorSpace == 1 || colorSpace == 65535) { ResourceManager resourceManager = new ResourceManager( _colorProfileResources, Assembly.GetAssembly(typeof(ColorContext)) ); byte[] sRGBProfile = (byte[])resourceManager.GetObject(_sRGBProfileName); // The existing ColorContext has already been initialized as Exif so we can't initialize it again // and instead must create a new one. using (FactoryMaker factoryMaker = new FactoryMaker()) { _colorContextHandle.Dispose(); _colorContextHandle = null; if (HRESULT.Failed(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext( factoryMaker.ImagingFactoryPtr, out _colorContextHandle)) ) { return; } if (HRESULT.Failed(IWICCC.InitializeFromMemory( _colorContextHandle, sRGBProfile, (uint)sRGBProfile.Length)) ) { return; } } // Finally, fill in _colorContextHelper FromRawBytes(sRGBProfile, sRGBProfile.Length, /* dontThrowException = */ true); } else if (Invariant.Strict) { Invariant.Assert(false, String.Format(CultureInfo.InvariantCulture, "IWICColorContext::GetExifColorSpace returned {0}.", colorSpace)); } break; default: if (Invariant.Strict) { Invariant.Assert(false, "IWICColorContext::GetType() returned WICColorContextUninitialized."); } break; } // SECURITY NOTE: This constructor does not set a Uri because the profile comes from raw file // data. Thus, we don't set _isProfileUriNotFromUser to true because we // don't want get_ProfileUri to demand permission to return null. Debug.Assert(_profileUri.Value == null); } ////// Creates a new ColorContext object from a .icm or .icc color profile specified by profileUri. /// ////// SecurityCritical: Calls the SecurityCritical method Initialize() /// PublicOK: doesn't reveal any sensitive information /// /// Specifies the URI of a color profile used by the newly created ColorContext. [SecurityCritical] public ColorContext(Uri profileUri) { Initialize(profileUri, /* isStandardProfileUriNotFromUser = */ false); } ////// Given a pixel format, this function will return the closest standard color space (sRGB, scRGB, etc) /// ////// SecurityCritical: Calls the SecurityCritical method GetStandardColorSpaceProfile() /// PublicOK: doesn't reveal any sensitive information /// [SecurityCritical] public ColorContext(PixelFormat pixelFormat) { switch (pixelFormat.Format) { case PixelFormatEnum.Default: case PixelFormatEnum.Indexed1: case PixelFormatEnum.Indexed2: case PixelFormatEnum.Indexed4: case PixelFormatEnum.Indexed8: case PixelFormatEnum.Bgr555: case PixelFormatEnum.Bgr565: case PixelFormatEnum.Bgr24: case PixelFormatEnum.Rgb24: case PixelFormatEnum.Bgr32: case PixelFormatEnum.Bgra32: case PixelFormatEnum.Pbgra32: default: Initialize(GetStandardColorSpaceProfile(), /* isStandardProfileUriNotFromUser = */ true); break; case PixelFormatEnum.Rgba64: case PixelFormatEnum.Prgba64: case PixelFormatEnum.Rgba128Float: case PixelFormatEnum.Prgba128Float: case PixelFormatEnum.BlackWhite: case PixelFormatEnum.Gray2: case PixelFormatEnum.Gray4: case PixelFormatEnum.Gray8: case PixelFormatEnum.Gray32Float: case PixelFormatEnum.Cmyk32: throw new NotSupportedException(); // standard scRGB profile does not exist yet } } #endregion #region Public Methods ////// Returns a memory stream to the color profile bits /// ////// SecurityCritical: This code calls unmanaged critical code /// PublicOK: We are exposing specific, non-sensitive data that came from /// a Uri or a bitmap. If the profile data came from a Uri, the /// WebRequest we did to get it demandend the appropriate /// permission and we did not expose the request or response. If it came /// from a bitmap, it may be cross-domain, but the restricted /// nature of the data combined with the fact that it has been /// inspected by WCS which is hardened against malicious data /// makes this OK. /// [SecurityCritical] public Stream OpenProfileStream() { // // 3.* backwards compat for a "bad" ColorContext. Now the helper is a // struct so when it's invalid we'll pretend it's a reference type. This // should only happen if the color profile is corrupt (see early exits in // ColorContext(SafeMILHandle) and FromRawBytes()). // if (_colorContextHelper.IsInvalid) { throw new NullReferenceException(); } uint profileSize = 0; _colorContextHelper.GetColorProfileFromHandle(null, ref profileSize); byte[] profile = new byte[profileSize]; _colorContextHelper.GetColorProfileFromHandle(profile, ref profileSize); return new MemoryStream(profile); } #endregion Public Methods #region Public Properties ////// ProfileUri /// ////// SecurityCritical: Returns the profile uri which could be a sensitive local path like %WINDIR% /// that shouldn't be given out in partial trust /// PublicOK: demands appropriate permissions /// public Uri ProfileUri { [SecurityCritical] get { Uri uri = _profileUri.Value; // // We don't need to demand permission if the user gave us the uri because the user // already knows the value. If the user didn't give us the value, then the uri has // to be a file path because we got it from GetStandardColorSpaceProfile // if (_isProfileUriNotFromUser.Value) { Invariant.Assert(uri.IsFile); SecurityHelper.DemandPathDiscovery(uri.LocalPath); } return uri; } } #endregion Public Properties #region Internal Properties ////// ProfileHandle /// ////// SecurityCritical: This comes out of an elevation needs to be critical and tracked. /// internal SafeProfileHandle ProfileHandle { [SecurityCritical] get { return _colorContextHelper.ProfileHandle; } } ////// ColorContextHandleHandle /// ////// SecurityCritical: Retrieves a critical data member (unmanaged pointer) /// internal SafeMILHandle ColorContextHandle { [SecurityCritical] get { return _colorContextHandle; } } ////// NumChannels /// ////// SecurityCritical: This code reference critical data /// SecurityTreatAsSafe: Even it reference the critical data but it only check it is null or valid /// return data (_numChannels) is safe /// internal int NumChannels { [SecurityCritical, SecurityTreatAsSafe] get { if (_colorContextHelper.IsInvalid) // sRGB or scRGB return 3; return _numChannels; } } ////// ColorType /// internal UInt32 ColorType { get { return (UInt32)_colorTypeFromChannels[NumChannels]; } } ////// ColorSpaceFamily /// ////// SecurityCritical: This code reference critical data /// SecurityTreatAsSafe: Even it reference the critical data but it only check it is null or valid /// return data (_colorSpaceFamily) is safe /// internal StandardColorSpace ColorSpaceFamily { [SecurityCritical, SecurityTreatAsSafe] get { if (_colorContextHelper.IsInvalid) // sRGB or scRGB { return StandardColorSpace.Srgb; } else { return _colorSpaceFamily; } } } ////// Returns false if the ColorContext hasn't been properly initialized due to a bad color profile. /// If this is false, use of this ColorContext will lead to exceptions. /// ////// SecurityCritical: Touches critical _colorContextHelper /// SecurityTreatAsSafe: Checking validity is simply a null check on the sensitive data /// internal bool IsValid { [SecurityCritical, SecurityTreatAsSafe] get { return !_colorContextHelper.IsInvalid; } } internal delegate int GetColorContextsDelegate(ref uint numContexts, IntPtr[] colorContextPtrs); ////// Helper method that will retrieve ColorContexts from an unmanaged object (e.g. BitmapDecoder or BitmapFrameDecode) /// ////// SecurityCritical: Calls unmanaged code /// [SecurityCritical] internal static IListGetColorContextsHelper(GetColorContextsDelegate getColorContexts) { uint numContexts = 0; List colorContextsList = null; int hr = getColorContexts(ref numContexts, null); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } if (numContexts > 0) { // GetColorContexts does not create new IWICColorContexts. Instead, it initializes existing // ones so we must create them beforehand. SafeMILHandle[] colorContextHandles = new SafeMILHandle[numContexts]; using (FactoryMaker factoryMaker = new FactoryMaker()) { for (uint i = 0; i < numContexts; ++i) { HRESULT.Check(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext(factoryMaker.ImagingFactoryPtr, out colorContextHandles[i])); } } // The Marshal is unable to handle SafeMILHandle[] so we will convert it to an IntPtr[] ourselves. { IntPtr[] colorContextPtrs = new IntPtr[numContexts]; for (uint i = 0; i < numContexts; ++i) { colorContextPtrs[i] = colorContextHandles[i].DangerousGetHandle(); } HRESULT.Check(getColorContexts(ref numContexts, colorContextPtrs)); } colorContextsList = new List ((int)numContexts); for (uint i = 0; i < numContexts; ++i) { colorContextsList.Add(new ColorContext(colorContextHandles[i])); } } return colorContextsList; } #endregion //----------------------------------------------------- // // Equality Methods/Properties // //----------------------------------------------------- #region Equality methods and Properties /// /// Equals method /// override public bool Equals(object obj) { ColorContext context = obj as ColorContext; return (context == this); } ////// GetHashCode /// ////// SecurityCritical: Touches critical _profileHeader /// PublicOK: The date the profile was authored is not considered sensitive /// [SecurityCritical] override public int GetHashCode() { // phDateTime_2 contains the minute and second that the profile was created. Obviously this // is not a great hash, but the compiler forces us to implement this due to us implementing // operator==. Plus, we don't see hashing ColorContexts as an important scenario. This // is good enough. return (int)_profileHeader.phDateTime_2; } ////// Operator== /// ////// SecurityCritical: Touches critical _profileHeader /// PublicOK: No information is revealed to the user /// [SecurityCritical] public static bool operator==(ColorContext context1, ColorContext context2) { object obj1 = context1; object obj2 = context2; if (obj1 == null && obj2 == null) { return true; } else if (obj1 != null && obj2 != null) { #pragma warning disable 6506 return ( (context1._profileHeader.phSize == context2._profileHeader.phSize) && (context1._profileHeader.phCMMType == context2._profileHeader.phCMMType) && (context1._profileHeader.phVersion == context2._profileHeader.phVersion) && (context1._profileHeader.phClass == context2._profileHeader.phClass) && (context1._profileHeader.phDataColorSpace == context2._profileHeader.phDataColorSpace) && (context1._profileHeader.phConnectionSpace == context2._profileHeader.phConnectionSpace) && (context1._profileHeader.phDateTime_0 == context2._profileHeader.phDateTime_0) && (context1._profileHeader.phDateTime_1 == context2._profileHeader.phDateTime_1) && (context1._profileHeader.phDateTime_2 == context2._profileHeader.phDateTime_2) && (context1._profileHeader.phSignature == context2._profileHeader.phSignature) && (context1._profileHeader.phPlatform == context2._profileHeader.phPlatform) && (context1._profileHeader.phProfileFlags == context2._profileHeader.phProfileFlags) && (context1._profileHeader.phManufacturer == context2._profileHeader.phManufacturer) && (context1._profileHeader.phModel == context2._profileHeader.phModel) && (context1._profileHeader.phAttributes_0 == context2._profileHeader.phAttributes_0) && (context1._profileHeader.phAttributes_1 == context2._profileHeader.phAttributes_1) && (context1._profileHeader.phRenderingIntent == context2._profileHeader.phRenderingIntent) && (context1._profileHeader.phIlluminant_0 == context2._profileHeader.phIlluminant_0) && (context1._profileHeader.phIlluminant_1 == context2._profileHeader.phIlluminant_1) && (context1._profileHeader.phIlluminant_2 == context2._profileHeader.phIlluminant_2) && (context1._profileHeader.phCreator == context2._profileHeader.phCreator) ); #pragma warning restore 6506 } else { return false; } } ////// Operator!= /// public static bool operator!=(ColorContext context1, ColorContext context2) { return !(context1 == context2); } #endregion #region Private Methods ////// Loads color profile given by profileUri /// ////// SecurityCritical: method calls SecurityCritical code, profileUri could contain /// sensitive path information /// [SecurityCritical] private void Initialize(Uri profileUri, bool isStandardProfileUriNotFromUser) { bool tryProfileFromResource = false; if (profileUri == null) { throw new ArgumentNullException("profileUri"); } if (!profileUri.IsAbsoluteUri) { throw new ArgumentException(SR.Get(SRID.UriNotAbsolute), "profileUri"); } _profileUri = new SecurityCriticalData(profileUri); _isProfileUriNotFromUser = new SecurityCriticalDataForSet (isStandardProfileUriNotFromUser); Stream profileStream = null; try { profileStream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(profileUri); } catch (WebException) { // // If we couldn't load the system's default color profile (e.g. in partial trust), load a color profile from // a resource so the image shows up at least. If the user specified a color profile and we weren't // able to load it, we'll fail to avoid letting the user use this resource fallback as a way to discover // files on disk. // if (isStandardProfileUriNotFromUser) { tryProfileFromResource = true; } } if (profileStream == null) { if (tryProfileFromResource) { ResourceManager resourceManager = new ResourceManager(_colorProfileResources, Assembly.GetAssembly(typeof(ColorContext))); byte[] sRGBProfile = (byte[])resourceManager.GetObject(_sRGBProfileName); profileStream = new MemoryStream(sRGBProfile); } else { // // SECURITY WARNING: This exception includes the profile URI which may contain sensitive information. However, as of right now, // this is safe because it can only happen when the URI is given to us by the user. // Invariant.Assert(!isStandardProfileUriNotFromUser); throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, profileUri.AbsolutePath), profileUri.AbsolutePath); } } FromStream(profileStream, profileUri.AbsolutePath); } /// /// Obtains the system color profile path /// ////// SecurityCritical: This code calls critical code (unmanaged) and returns /// path information that can be set by an outside party and/or may include /// sensitive paths like %WINDIR% /// [SecurityCritical] private static Uri GetStandardColorSpaceProfile() { const int SIZE = NativeMethods.MAX_PATH; uint dwProfileID = (uint)NativeMethods.ColorSpace.SPACE_sRGB; uint bufferSize = SIZE; StringBuilder buffer = new StringBuilder(SIZE); HRESULT.Check(UnsafeNativeMethodsMilCoreApi.Mscms.GetStandardColorSpaceProfile(IntPtr.Zero, dwProfileID, buffer, out bufferSize)); Uri profilePath; string profilePathString = buffer.ToString(); if (!Uri.TryCreate(profilePathString, UriKind.Absolute, out profilePath)) { // // GetStandardColorSpaceProfile() returns whatever was given to SetStandardColorSpaceProfile(). // If it were set to a relative path by the user, we should throw an exception to avoid any possible // security issues. However, the Vista control panel uses the same API and sometimes likes to set // relative paths. Since we can't tell the difference and we want people to be able to change // their color profile from the control panel, we'll tack on the system directory. // // bufferSize was modified by GetStandardColorSpaceProfile so set it again bufferSize = SIZE; HRESULT.Check(UnsafeNativeMethodsMilCoreApi.Mscms.GetColorDirectory(IntPtr.Zero, buffer, out bufferSize)); profilePath = new Uri(Path.Combine(buffer.ToString(), profilePathString)); } return profilePath; } ////// SecurityCritical: This code calls critical unmanaged code to create resources /// [SecurityCritical] private void FromStream(Stream stm, string filename) { Debug.Assert(stm != null); int bufferSize = _bufferSizeIncrement; if (stm.CanSeek) { bufferSize = (int)stm.Length + 1; // If this stream is seekable (most cases), we will only have one buffer alloc and read below // otherwise, we will incrementally grow the buffer and read until end of profile. // profiles are typcially small, so usually one allocation will suffice } byte[] rawBytes = new byte[bufferSize]; int numBytesRead = 0; while (bufferSize < _maximumColorContextLength) { numBytesRead += stm.Read(rawBytes, numBytesRead, bufferSize - numBytesRead); if (numBytesRead < bufferSize) { FromRawBytes(rawBytes, numBytesRead, /* dontThrowException = */ false); using (FactoryMaker factoryMaker = new FactoryMaker()) { HRESULT.Check(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext(factoryMaker.ImagingFactoryPtr, out _colorContextHandle)); HRESULT.Check(IWICCC.InitializeFromMemory(_colorContextHandle, rawBytes, (uint)numBytesRead)); } return; } else { bufferSize += _bufferSizeIncrement; byte[] newRawBytes = new byte[bufferSize]; rawBytes.CopyTo(newRawBytes, 0); rawBytes = newRawBytes; } } throw new ArgumentException(SR.Get(SRID.ColorContext_FileTooLarge), filename); } /// Note: often the data buffer is larger than the actual data in it. /// /// dontThrowException is for preserving the 3.* behavior of ColorContext(SafeMILHandle) /// ////// SecurityCritical: This code calls critical code (unmanaged), contains unsafe code block /// [SecurityCritical] private void FromRawBytes(byte[] data, int dataLength, bool dontThrowException) { Invariant.Assert(dataLength <= data.Length); Invariant.Assert(dataLength >= 0); UnsafeNativeMethods.PROFILEHEADER header; UnsafeNativeMethods.PROFILE profile; unsafe { fixed (void *dataPtr = data) { profile.dwType = NativeMethods.ProfileType.PROFILE_MEMBUFFER; profile.pProfileData = dataPtr; profile.cbDataSize = (uint)dataLength; _colorContextHelper.OpenColorProfile(ref profile); if (_colorContextHelper.IsInvalid) { if (dontThrowException) { return; } else { HRESULT.Check(Marshal.GetHRForLastWin32Error()); } } } } if (!_colorContextHelper.GetColorProfileHeader(out header)) { if (dontThrowException) { return; } else { HRESULT.Check(Marshal.GetHRForLastWin32Error()); } } // Copy the important stuff from the header into our smaller cache _profileHeader.phSize = header.phSize; _profileHeader.phCMMType = header.phCMMType; _profileHeader.phVersion = header.phVersion; _profileHeader.phClass = header.phClass; _profileHeader.phDataColorSpace = header.phDataColorSpace; _profileHeader.phConnectionSpace = header.phConnectionSpace; _profileHeader.phDateTime_0 = header.phDateTime_0; _profileHeader.phDateTime_1 = header.phDateTime_1; _profileHeader.phDateTime_2 = header.phDateTime_2; _profileHeader.phSignature = header.phSignature; _profileHeader.phPlatform = header.phPlatform; _profileHeader.phProfileFlags = header.phProfileFlags; _profileHeader.phManufacturer = header.phManufacturer; _profileHeader.phModel = header.phModel; _profileHeader.phAttributes_0 = header.phAttributes_0; _profileHeader.phAttributes_1 = header.phAttributes_1; _profileHeader.phRenderingIntent = header.phRenderingIntent; _profileHeader.phIlluminant_0 = header.phIlluminant_0; _profileHeader.phIlluminant_1 = header.phIlluminant_1; _profileHeader.phIlluminant_2 = header.phIlluminant_2; _profileHeader.phCreator = header.phCreator; switch (_profileHeader.phDataColorSpace) { case NativeMethods.ColorSpace.SPACE_XYZ: case NativeMethods.ColorSpace.SPACE_Lab: case NativeMethods.ColorSpace.SPACE_Luv: case NativeMethods.ColorSpace.SPACE_YCbCr: case NativeMethods.ColorSpace.SPACE_Yxy: case NativeMethods.ColorSpace.SPACE_HSV: case NativeMethods.ColorSpace.SPACE_HLS: case NativeMethods.ColorSpace.SPACE_CMY: _numChannels = 3; _colorSpaceFamily = StandardColorSpace.Unknown; break; case NativeMethods.ColorSpace.SPACE_RGB: _colorSpaceFamily = StandardColorSpace.Rgb; _numChannels = 3; break; case NativeMethods.ColorSpace.SPACE_GRAY: _colorSpaceFamily = StandardColorSpace.Gray; _numChannels = 1; break; case NativeMethods.ColorSpace.SPACE_CMYK: _colorSpaceFamily = StandardColorSpace.Cmyk; _numChannels = 4; break; case NativeMethods.ColorSpace.SPACE_2_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 2; break; case NativeMethods.ColorSpace.SPACE_3_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 3; break; case NativeMethods.ColorSpace.SPACE_4_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 4; break; case NativeMethods.ColorSpace.SPACE_5_CHANNEL: _numChannels = 5; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_6_CHANNEL: _numChannels = 6; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_7_CHANNEL: _numChannels = 7; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_8_CHANNEL: _numChannels = 8; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_9_CHANNEL: _numChannels = 9; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_A_CHANNEL: _numChannels = 10; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_B_CHANNEL: _numChannels = 11; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_C_CHANNEL: _numChannels = 12; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_D_CHANNEL: _numChannels = 13; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_E_CHANNEL: _numChannels = 14; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_F_CHANNEL: _numChannels = 15; _colorSpaceFamily = StandardColorSpace.Multichannel; break; default: _numChannels = 0; _colorSpaceFamily = StandardColorSpace.Unknown; break; } } #endregion #region Private Fields ////// SecurityCritical: This comes out of an elevation needs to be critical and tracked. /// [SecurityCritical] private ColorContextHelper _colorContextHelper; private StandardColorSpace _colorSpaceFamily; private int _numChannels; ////// SecurityCritical: May contain sensitive path information like %WINDIR% /// private SecurityCriticalData_profileUri; /// /// SecurityCritical: Determines whether or not _profileUri contains sensitive data /// that we need to demand access to /// private SecurityCriticalDataForSet_isProfileUriNotFromUser; /// /// SecurityCritical: Retrieved from unmanaged code /// [SecurityCritical] private AbbreviatedPROFILEHEADER _profileHeader; ////// SecurityCritical: Unmanaged IWICColorContext handle /// [SecurityCritical] private SafeMILHandle _colorContextHandle; private const int _bufferSizeIncrement = 1024 * 1024; // 1 Mb private const int _maximumColorContextLength = _bufferSizeIncrement * 32; // 32 Mb private readonly static NativeMethods.COLORTYPE[] _colorTypeFromChannels = new NativeMethods.COLORTYPE[9] { NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_3_CHANNEL, NativeMethods.COLORTYPE.COLOR_CMYK, NativeMethods.COLORTYPE.COLOR_5_CHANNEL, NativeMethods.COLORTYPE.COLOR_6_CHANNEL, NativeMethods.COLORTYPE.COLOR_7_CHANNEL, NativeMethods.COLORTYPE.COLOR_8_CHANNEL }; private readonly static string _colorProfileResources = "ColorProfiles"; private readonly static string _sRGBProfileName = "sRGB_icm"; [StructLayout(LayoutKind.Sequential)] private struct AbbreviatedPROFILEHEADER { public uint phSize; // profile size in bytes public uint phCMMType; // CMM for this profile public uint phVersion; // profile format version number public uint phClass; // type of profile public NativeMethods.ColorSpace phDataColorSpace; // color space of data public uint phConnectionSpace; // PCS public uint phDateTime_0; // date profile was created public uint phDateTime_1; // date profile was created public uint phDateTime_2; // date profile was created public uint phSignature; // magic number ("Reserved for internal use.") public uint phPlatform; // primary platform public uint phProfileFlags; // various bit settings public uint phManufacturer; // device manufacturer public uint phModel; // device model number public uint phAttributes_0; // device attributes public uint phAttributes_1; // device attributes public uint phRenderingIntent; // rendering intent public uint phIlluminant_0; // profile illuminant public uint phIlluminant_1; // profile illuminant public uint phIlluminant_2; // profile illuminant public uint phCreator; // profile creator // Not including the reserved bits because we don't want to unnecessarily // increase the size of ColorContext // public byte phReserved[44]; }; internal enum StandardColorSpace : int { Unknown = 0, Srgb = 1, ScRgb = 2, Rgb = 3, Cmyk = 4, Gray = 6, Multichannel = 7 } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // Microsoft Windows Client Platform // Copyright (c) Microsoft Corporation, All Rights Reserved. // // File: ColorContext.cs //----------------------------------------------------------------------------- #pragma warning disable 1634, 1691 // Allow suppression of certain presharp messages using System; using MS.Internal; using MS.Internal.PresentationCore; using MS.Win32; using System.IO; using System.Runtime.InteropServices; using System.Windows.Media; using System.Resources; using System.Security; using System.Security.Permissions; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Reflection; using System.Diagnostics; using System.Globalization; using Microsoft.Win32.SafeHandles; using System.Net; using System.IO.Packaging; using System.Windows.Navigation; using System.Diagnostics.CodeAnalysis; using System.Text; using SR = MS.Internal.PresentationCore.SR; using SRID = MS.Internal.PresentationCore.SRID; using UnsafeNativeMethodsMilCoreApi = MS.Win32.PresentationCore.UnsafeNativeMethods; using IWICCC = MS.Win32.PresentationCore.UnsafeNativeMethods.IWICColorContext; namespace System.Windows.Media { ////// Color Context /// public class ColorContext { #region Constructors ////// Create a ColorContext from an unmanaged color context /// ////// SecurityCritical: This code sets a critical data member, calls critical code /// [SecurityCritical] private ColorContext(SafeMILHandle colorContextHandle) { _colorContextHandle = colorContextHandle; // // For 3.* backwards compat, we aren't going to HRESULT.Check() anywhere because // that could introduce new exceptions. If anything fails, _colorContextHelper // will be invalid and we'll emulate the old failure behavior later in // OpenProfileStream() // IWICCC.WICColorContextType type; if (HRESULT.Failed(IWICCC.GetType(_colorContextHandle, out type))) { return; } switch (type) { case IWICCC.WICColorContextType.WICColorContextProfile: uint cbProfileActual; int hr = IWICCC.GetProfileBytes(_colorContextHandle, 0, null, out cbProfileActual); if (HRESULT.Succeeded(hr) && cbProfileActual != 0) { byte[] profileData = new byte[cbProfileActual]; if (HRESULT.Failed(IWICCC.GetProfileBytes( _colorContextHandle, cbProfileActual, profileData, out cbProfileActual)) ) { return; } FromRawBytes(profileData, (int)cbProfileActual, /* dontThrowException = */ true); } break; case IWICCC.WICColorContextType.WICColorContextExifColorSpace: uint colorSpace; if (HRESULT.Failed(IWICCC.GetExifColorSpace(_colorContextHandle, out colorSpace))) { return; } // // From MSDN: // "1" is sRGB. We will use our built-in sRGB profile. // "2" is Adobe RGB. WIC says we should never see this because they are nonstandard and instead a // real profile will be returned. // "3-65534" is unused. // // From the Exif spec: // B. Tag Relating to Color Space // ColorSpace // // The color space information tag (ColorSpace) is always recorded as the color space specifier. // Normally sRGB (=1) is used to define the color space based on the PC monitor conditions and environment. If a // color space other than sRGB is used, Uncalibrated (=FFFF.H) is set. Image data recorded as Uncalibrated can be // treated as sRGB when it is converted to Flashpix. On sRGB see Annex E. // Tag = 40961 (A001.H) // Type = SHORT // Count = 1 // 1 = sRGB // FFFF.H = Uncalibrated // // So for 65535 we will return sRGB since it is acceptible rather than having an invalid ColorContext. The Exif // CC should always be the second one so the real one is given priority. Alternatively, we could ignore the // uncalibrated CC but that would be a breaking change with 3.* (returning 1 instead of 2). // // If anything other than 1 or 65535 happens, _colorContextHelper will remain invalid and we will emulate // the old crash behavior in OpenProfileStream(). // if (colorSpace == 1 || colorSpace == 65535) { ResourceManager resourceManager = new ResourceManager( _colorProfileResources, Assembly.GetAssembly(typeof(ColorContext)) ); byte[] sRGBProfile = (byte[])resourceManager.GetObject(_sRGBProfileName); // The existing ColorContext has already been initialized as Exif so we can't initialize it again // and instead must create a new one. using (FactoryMaker factoryMaker = new FactoryMaker()) { _colorContextHandle.Dispose(); _colorContextHandle = null; if (HRESULT.Failed(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext( factoryMaker.ImagingFactoryPtr, out _colorContextHandle)) ) { return; } if (HRESULT.Failed(IWICCC.InitializeFromMemory( _colorContextHandle, sRGBProfile, (uint)sRGBProfile.Length)) ) { return; } } // Finally, fill in _colorContextHelper FromRawBytes(sRGBProfile, sRGBProfile.Length, /* dontThrowException = */ true); } else if (Invariant.Strict) { Invariant.Assert(false, String.Format(CultureInfo.InvariantCulture, "IWICColorContext::GetExifColorSpace returned {0}.", colorSpace)); } break; default: if (Invariant.Strict) { Invariant.Assert(false, "IWICColorContext::GetType() returned WICColorContextUninitialized."); } break; } // SECURITY NOTE: This constructor does not set a Uri because the profile comes from raw file // data. Thus, we don't set _isProfileUriNotFromUser to true because we // don't want get_ProfileUri to demand permission to return null. Debug.Assert(_profileUri.Value == null); } ////// Creates a new ColorContext object from a .icm or .icc color profile specified by profileUri. /// ////// SecurityCritical: Calls the SecurityCritical method Initialize() /// PublicOK: doesn't reveal any sensitive information /// /// Specifies the URI of a color profile used by the newly created ColorContext. [SecurityCritical] public ColorContext(Uri profileUri) { Initialize(profileUri, /* isStandardProfileUriNotFromUser = */ false); } ////// Given a pixel format, this function will return the closest standard color space (sRGB, scRGB, etc) /// ////// SecurityCritical: Calls the SecurityCritical method GetStandardColorSpaceProfile() /// PublicOK: doesn't reveal any sensitive information /// [SecurityCritical] public ColorContext(PixelFormat pixelFormat) { switch (pixelFormat.Format) { case PixelFormatEnum.Default: case PixelFormatEnum.Indexed1: case PixelFormatEnum.Indexed2: case PixelFormatEnum.Indexed4: case PixelFormatEnum.Indexed8: case PixelFormatEnum.Bgr555: case PixelFormatEnum.Bgr565: case PixelFormatEnum.Bgr24: case PixelFormatEnum.Rgb24: case PixelFormatEnum.Bgr32: case PixelFormatEnum.Bgra32: case PixelFormatEnum.Pbgra32: default: Initialize(GetStandardColorSpaceProfile(), /* isStandardProfileUriNotFromUser = */ true); break; case PixelFormatEnum.Rgba64: case PixelFormatEnum.Prgba64: case PixelFormatEnum.Rgba128Float: case PixelFormatEnum.Prgba128Float: case PixelFormatEnum.BlackWhite: case PixelFormatEnum.Gray2: case PixelFormatEnum.Gray4: case PixelFormatEnum.Gray8: case PixelFormatEnum.Gray32Float: case PixelFormatEnum.Cmyk32: throw new NotSupportedException(); // standard scRGB profile does not exist yet } } #endregion #region Public Methods ////// Returns a memory stream to the color profile bits /// ////// SecurityCritical: This code calls unmanaged critical code /// PublicOK: We are exposing specific, non-sensitive data that came from /// a Uri or a bitmap. If the profile data came from a Uri, the /// WebRequest we did to get it demandend the appropriate /// permission and we did not expose the request or response. If it came /// from a bitmap, it may be cross-domain, but the restricted /// nature of the data combined with the fact that it has been /// inspected by WCS which is hardened against malicious data /// makes this OK. /// [SecurityCritical] public Stream OpenProfileStream() { // // 3.* backwards compat for a "bad" ColorContext. Now the helper is a // struct so when it's invalid we'll pretend it's a reference type. This // should only happen if the color profile is corrupt (see early exits in // ColorContext(SafeMILHandle) and FromRawBytes()). // if (_colorContextHelper.IsInvalid) { throw new NullReferenceException(); } uint profileSize = 0; _colorContextHelper.GetColorProfileFromHandle(null, ref profileSize); byte[] profile = new byte[profileSize]; _colorContextHelper.GetColorProfileFromHandle(profile, ref profileSize); return new MemoryStream(profile); } #endregion Public Methods #region Public Properties ////// ProfileUri /// ////// SecurityCritical: Returns the profile uri which could be a sensitive local path like %WINDIR% /// that shouldn't be given out in partial trust /// PublicOK: demands appropriate permissions /// public Uri ProfileUri { [SecurityCritical] get { Uri uri = _profileUri.Value; // // We don't need to demand permission if the user gave us the uri because the user // already knows the value. If the user didn't give us the value, then the uri has // to be a file path because we got it from GetStandardColorSpaceProfile // if (_isProfileUriNotFromUser.Value) { Invariant.Assert(uri.IsFile); SecurityHelper.DemandPathDiscovery(uri.LocalPath); } return uri; } } #endregion Public Properties #region Internal Properties ////// ProfileHandle /// ////// SecurityCritical: This comes out of an elevation needs to be critical and tracked. /// internal SafeProfileHandle ProfileHandle { [SecurityCritical] get { return _colorContextHelper.ProfileHandle; } } ////// ColorContextHandleHandle /// ////// SecurityCritical: Retrieves a critical data member (unmanaged pointer) /// internal SafeMILHandle ColorContextHandle { [SecurityCritical] get { return _colorContextHandle; } } ////// NumChannels /// ////// SecurityCritical: This code reference critical data /// SecurityTreatAsSafe: Even it reference the critical data but it only check it is null or valid /// return data (_numChannels) is safe /// internal int NumChannels { [SecurityCritical, SecurityTreatAsSafe] get { if (_colorContextHelper.IsInvalid) // sRGB or scRGB return 3; return _numChannels; } } ////// ColorType /// internal UInt32 ColorType { get { return (UInt32)_colorTypeFromChannels[NumChannels]; } } ////// ColorSpaceFamily /// ////// SecurityCritical: This code reference critical data /// SecurityTreatAsSafe: Even it reference the critical data but it only check it is null or valid /// return data (_colorSpaceFamily) is safe /// internal StandardColorSpace ColorSpaceFamily { [SecurityCritical, SecurityTreatAsSafe] get { if (_colorContextHelper.IsInvalid) // sRGB or scRGB { return StandardColorSpace.Srgb; } else { return _colorSpaceFamily; } } } ////// Returns false if the ColorContext hasn't been properly initialized due to a bad color profile. /// If this is false, use of this ColorContext will lead to exceptions. /// ////// SecurityCritical: Touches critical _colorContextHelper /// SecurityTreatAsSafe: Checking validity is simply a null check on the sensitive data /// internal bool IsValid { [SecurityCritical, SecurityTreatAsSafe] get { return !_colorContextHelper.IsInvalid; } } internal delegate int GetColorContextsDelegate(ref uint numContexts, IntPtr[] colorContextPtrs); ////// Helper method that will retrieve ColorContexts from an unmanaged object (e.g. BitmapDecoder or BitmapFrameDecode) /// ////// SecurityCritical: Calls unmanaged code /// [SecurityCritical] internal static IListGetColorContextsHelper(GetColorContextsDelegate getColorContexts) { uint numContexts = 0; List colorContextsList = null; int hr = getColorContexts(ref numContexts, null); if (hr != (int)WinCodecErrors.WINCODEC_ERR_UNSUPPORTEDOPERATION) { HRESULT.Check(hr); } if (numContexts > 0) { // GetColorContexts does not create new IWICColorContexts. Instead, it initializes existing // ones so we must create them beforehand. SafeMILHandle[] colorContextHandles = new SafeMILHandle[numContexts]; using (FactoryMaker factoryMaker = new FactoryMaker()) { for (uint i = 0; i < numContexts; ++i) { HRESULT.Check(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext(factoryMaker.ImagingFactoryPtr, out colorContextHandles[i])); } } // The Marshal is unable to handle SafeMILHandle[] so we will convert it to an IntPtr[] ourselves. { IntPtr[] colorContextPtrs = new IntPtr[numContexts]; for (uint i = 0; i < numContexts; ++i) { colorContextPtrs[i] = colorContextHandles[i].DangerousGetHandle(); } HRESULT.Check(getColorContexts(ref numContexts, colorContextPtrs)); } colorContextsList = new List ((int)numContexts); for (uint i = 0; i < numContexts; ++i) { colorContextsList.Add(new ColorContext(colorContextHandles[i])); } } return colorContextsList; } #endregion //----------------------------------------------------- // // Equality Methods/Properties // //----------------------------------------------------- #region Equality methods and Properties /// /// Equals method /// override public bool Equals(object obj) { ColorContext context = obj as ColorContext; return (context == this); } ////// GetHashCode /// ////// SecurityCritical: Touches critical _profileHeader /// PublicOK: The date the profile was authored is not considered sensitive /// [SecurityCritical] override public int GetHashCode() { // phDateTime_2 contains the minute and second that the profile was created. Obviously this // is not a great hash, but the compiler forces us to implement this due to us implementing // operator==. Plus, we don't see hashing ColorContexts as an important scenario. This // is good enough. return (int)_profileHeader.phDateTime_2; } ////// Operator== /// ////// SecurityCritical: Touches critical _profileHeader /// PublicOK: No information is revealed to the user /// [SecurityCritical] public static bool operator==(ColorContext context1, ColorContext context2) { object obj1 = context1; object obj2 = context2; if (obj1 == null && obj2 == null) { return true; } else if (obj1 != null && obj2 != null) { #pragma warning disable 6506 return ( (context1._profileHeader.phSize == context2._profileHeader.phSize) && (context1._profileHeader.phCMMType == context2._profileHeader.phCMMType) && (context1._profileHeader.phVersion == context2._profileHeader.phVersion) && (context1._profileHeader.phClass == context2._profileHeader.phClass) && (context1._profileHeader.phDataColorSpace == context2._profileHeader.phDataColorSpace) && (context1._profileHeader.phConnectionSpace == context2._profileHeader.phConnectionSpace) && (context1._profileHeader.phDateTime_0 == context2._profileHeader.phDateTime_0) && (context1._profileHeader.phDateTime_1 == context2._profileHeader.phDateTime_1) && (context1._profileHeader.phDateTime_2 == context2._profileHeader.phDateTime_2) && (context1._profileHeader.phSignature == context2._profileHeader.phSignature) && (context1._profileHeader.phPlatform == context2._profileHeader.phPlatform) && (context1._profileHeader.phProfileFlags == context2._profileHeader.phProfileFlags) && (context1._profileHeader.phManufacturer == context2._profileHeader.phManufacturer) && (context1._profileHeader.phModel == context2._profileHeader.phModel) && (context1._profileHeader.phAttributes_0 == context2._profileHeader.phAttributes_0) && (context1._profileHeader.phAttributes_1 == context2._profileHeader.phAttributes_1) && (context1._profileHeader.phRenderingIntent == context2._profileHeader.phRenderingIntent) && (context1._profileHeader.phIlluminant_0 == context2._profileHeader.phIlluminant_0) && (context1._profileHeader.phIlluminant_1 == context2._profileHeader.phIlluminant_1) && (context1._profileHeader.phIlluminant_2 == context2._profileHeader.phIlluminant_2) && (context1._profileHeader.phCreator == context2._profileHeader.phCreator) ); #pragma warning restore 6506 } else { return false; } } ////// Operator!= /// public static bool operator!=(ColorContext context1, ColorContext context2) { return !(context1 == context2); } #endregion #region Private Methods ////// Loads color profile given by profileUri /// ////// SecurityCritical: method calls SecurityCritical code, profileUri could contain /// sensitive path information /// [SecurityCritical] private void Initialize(Uri profileUri, bool isStandardProfileUriNotFromUser) { bool tryProfileFromResource = false; if (profileUri == null) { throw new ArgumentNullException("profileUri"); } if (!profileUri.IsAbsoluteUri) { throw new ArgumentException(SR.Get(SRID.UriNotAbsolute), "profileUri"); } _profileUri = new SecurityCriticalData(profileUri); _isProfileUriNotFromUser = new SecurityCriticalDataForSet (isStandardProfileUriNotFromUser); Stream profileStream = null; try { profileStream = WpfWebRequestHelper.CreateRequestAndGetResponseStream(profileUri); } catch (WebException) { // // If we couldn't load the system's default color profile (e.g. in partial trust), load a color profile from // a resource so the image shows up at least. If the user specified a color profile and we weren't // able to load it, we'll fail to avoid letting the user use this resource fallback as a way to discover // files on disk. // if (isStandardProfileUriNotFromUser) { tryProfileFromResource = true; } } if (profileStream == null) { if (tryProfileFromResource) { ResourceManager resourceManager = new ResourceManager(_colorProfileResources, Assembly.GetAssembly(typeof(ColorContext))); byte[] sRGBProfile = (byte[])resourceManager.GetObject(_sRGBProfileName); profileStream = new MemoryStream(sRGBProfile); } else { // // SECURITY WARNING: This exception includes the profile URI which may contain sensitive information. However, as of right now, // this is safe because it can only happen when the URI is given to us by the user. // Invariant.Assert(!isStandardProfileUriNotFromUser); throw new FileNotFoundException(SR.Get(SRID.FileNotFoundExceptionWithFileName, profileUri.AbsolutePath), profileUri.AbsolutePath); } } FromStream(profileStream, profileUri.AbsolutePath); } /// /// Obtains the system color profile path /// ////// SecurityCritical: This code calls critical code (unmanaged) and returns /// path information that can be set by an outside party and/or may include /// sensitive paths like %WINDIR% /// [SecurityCritical] private static Uri GetStandardColorSpaceProfile() { const int SIZE = NativeMethods.MAX_PATH; uint dwProfileID = (uint)NativeMethods.ColorSpace.SPACE_sRGB; uint bufferSize = SIZE; StringBuilder buffer = new StringBuilder(SIZE); HRESULT.Check(UnsafeNativeMethodsMilCoreApi.Mscms.GetStandardColorSpaceProfile(IntPtr.Zero, dwProfileID, buffer, out bufferSize)); Uri profilePath; string profilePathString = buffer.ToString(); if (!Uri.TryCreate(profilePathString, UriKind.Absolute, out profilePath)) { // // GetStandardColorSpaceProfile() returns whatever was given to SetStandardColorSpaceProfile(). // If it were set to a relative path by the user, we should throw an exception to avoid any possible // security issues. However, the Vista control panel uses the same API and sometimes likes to set // relative paths. Since we can't tell the difference and we want people to be able to change // their color profile from the control panel, we'll tack on the system directory. // // bufferSize was modified by GetStandardColorSpaceProfile so set it again bufferSize = SIZE; HRESULT.Check(UnsafeNativeMethodsMilCoreApi.Mscms.GetColorDirectory(IntPtr.Zero, buffer, out bufferSize)); profilePath = new Uri(Path.Combine(buffer.ToString(), profilePathString)); } return profilePath; } ////// SecurityCritical: This code calls critical unmanaged code to create resources /// [SecurityCritical] private void FromStream(Stream stm, string filename) { Debug.Assert(stm != null); int bufferSize = _bufferSizeIncrement; if (stm.CanSeek) { bufferSize = (int)stm.Length + 1; // If this stream is seekable (most cases), we will only have one buffer alloc and read below // otherwise, we will incrementally grow the buffer and read until end of profile. // profiles are typcially small, so usually one allocation will suffice } byte[] rawBytes = new byte[bufferSize]; int numBytesRead = 0; while (bufferSize < _maximumColorContextLength) { numBytesRead += stm.Read(rawBytes, numBytesRead, bufferSize - numBytesRead); if (numBytesRead < bufferSize) { FromRawBytes(rawBytes, numBytesRead, /* dontThrowException = */ false); using (FactoryMaker factoryMaker = new FactoryMaker()) { HRESULT.Check(UnsafeNativeMethodsMilCoreApi.WICCodec.CreateColorContext(factoryMaker.ImagingFactoryPtr, out _colorContextHandle)); HRESULT.Check(IWICCC.InitializeFromMemory(_colorContextHandle, rawBytes, (uint)numBytesRead)); } return; } else { bufferSize += _bufferSizeIncrement; byte[] newRawBytes = new byte[bufferSize]; rawBytes.CopyTo(newRawBytes, 0); rawBytes = newRawBytes; } } throw new ArgumentException(SR.Get(SRID.ColorContext_FileTooLarge), filename); } /// Note: often the data buffer is larger than the actual data in it. /// /// dontThrowException is for preserving the 3.* behavior of ColorContext(SafeMILHandle) /// ////// SecurityCritical: This code calls critical code (unmanaged), contains unsafe code block /// [SecurityCritical] private void FromRawBytes(byte[] data, int dataLength, bool dontThrowException) { Invariant.Assert(dataLength <= data.Length); Invariant.Assert(dataLength >= 0); UnsafeNativeMethods.PROFILEHEADER header; UnsafeNativeMethods.PROFILE profile; unsafe { fixed (void *dataPtr = data) { profile.dwType = NativeMethods.ProfileType.PROFILE_MEMBUFFER; profile.pProfileData = dataPtr; profile.cbDataSize = (uint)dataLength; _colorContextHelper.OpenColorProfile(ref profile); if (_colorContextHelper.IsInvalid) { if (dontThrowException) { return; } else { HRESULT.Check(Marshal.GetHRForLastWin32Error()); } } } } if (!_colorContextHelper.GetColorProfileHeader(out header)) { if (dontThrowException) { return; } else { HRESULT.Check(Marshal.GetHRForLastWin32Error()); } } // Copy the important stuff from the header into our smaller cache _profileHeader.phSize = header.phSize; _profileHeader.phCMMType = header.phCMMType; _profileHeader.phVersion = header.phVersion; _profileHeader.phClass = header.phClass; _profileHeader.phDataColorSpace = header.phDataColorSpace; _profileHeader.phConnectionSpace = header.phConnectionSpace; _profileHeader.phDateTime_0 = header.phDateTime_0; _profileHeader.phDateTime_1 = header.phDateTime_1; _profileHeader.phDateTime_2 = header.phDateTime_2; _profileHeader.phSignature = header.phSignature; _profileHeader.phPlatform = header.phPlatform; _profileHeader.phProfileFlags = header.phProfileFlags; _profileHeader.phManufacturer = header.phManufacturer; _profileHeader.phModel = header.phModel; _profileHeader.phAttributes_0 = header.phAttributes_0; _profileHeader.phAttributes_1 = header.phAttributes_1; _profileHeader.phRenderingIntent = header.phRenderingIntent; _profileHeader.phIlluminant_0 = header.phIlluminant_0; _profileHeader.phIlluminant_1 = header.phIlluminant_1; _profileHeader.phIlluminant_2 = header.phIlluminant_2; _profileHeader.phCreator = header.phCreator; switch (_profileHeader.phDataColorSpace) { case NativeMethods.ColorSpace.SPACE_XYZ: case NativeMethods.ColorSpace.SPACE_Lab: case NativeMethods.ColorSpace.SPACE_Luv: case NativeMethods.ColorSpace.SPACE_YCbCr: case NativeMethods.ColorSpace.SPACE_Yxy: case NativeMethods.ColorSpace.SPACE_HSV: case NativeMethods.ColorSpace.SPACE_HLS: case NativeMethods.ColorSpace.SPACE_CMY: _numChannels = 3; _colorSpaceFamily = StandardColorSpace.Unknown; break; case NativeMethods.ColorSpace.SPACE_RGB: _colorSpaceFamily = StandardColorSpace.Rgb; _numChannels = 3; break; case NativeMethods.ColorSpace.SPACE_GRAY: _colorSpaceFamily = StandardColorSpace.Gray; _numChannels = 1; break; case NativeMethods.ColorSpace.SPACE_CMYK: _colorSpaceFamily = StandardColorSpace.Cmyk; _numChannels = 4; break; case NativeMethods.ColorSpace.SPACE_2_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 2; break; case NativeMethods.ColorSpace.SPACE_3_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 3; break; case NativeMethods.ColorSpace.SPACE_4_CHANNEL: _colorSpaceFamily = StandardColorSpace.Multichannel; _numChannels = 4; break; case NativeMethods.ColorSpace.SPACE_5_CHANNEL: _numChannels = 5; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_6_CHANNEL: _numChannels = 6; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_7_CHANNEL: _numChannels = 7; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_8_CHANNEL: _numChannels = 8; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_9_CHANNEL: _numChannels = 9; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_A_CHANNEL: _numChannels = 10; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_B_CHANNEL: _numChannels = 11; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_C_CHANNEL: _numChannels = 12; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_D_CHANNEL: _numChannels = 13; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_E_CHANNEL: _numChannels = 14; _colorSpaceFamily = StandardColorSpace.Multichannel; break; case NativeMethods.ColorSpace.SPACE_F_CHANNEL: _numChannels = 15; _colorSpaceFamily = StandardColorSpace.Multichannel; break; default: _numChannels = 0; _colorSpaceFamily = StandardColorSpace.Unknown; break; } } #endregion #region Private Fields ////// SecurityCritical: This comes out of an elevation needs to be critical and tracked. /// [SecurityCritical] private ColorContextHelper _colorContextHelper; private StandardColorSpace _colorSpaceFamily; private int _numChannels; ////// SecurityCritical: May contain sensitive path information like %WINDIR% /// private SecurityCriticalData_profileUri; /// /// SecurityCritical: Determines whether or not _profileUri contains sensitive data /// that we need to demand access to /// private SecurityCriticalDataForSet_isProfileUriNotFromUser; /// /// SecurityCritical: Retrieved from unmanaged code /// [SecurityCritical] private AbbreviatedPROFILEHEADER _profileHeader; ////// SecurityCritical: Unmanaged IWICColorContext handle /// [SecurityCritical] private SafeMILHandle _colorContextHandle; private const int _bufferSizeIncrement = 1024 * 1024; // 1 Mb private const int _maximumColorContextLength = _bufferSizeIncrement * 32; // 32 Mb private readonly static NativeMethods.COLORTYPE[] _colorTypeFromChannels = new NativeMethods.COLORTYPE[9] { NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_UNDEFINED, NativeMethods.COLORTYPE.COLOR_3_CHANNEL, NativeMethods.COLORTYPE.COLOR_CMYK, NativeMethods.COLORTYPE.COLOR_5_CHANNEL, NativeMethods.COLORTYPE.COLOR_6_CHANNEL, NativeMethods.COLORTYPE.COLOR_7_CHANNEL, NativeMethods.COLORTYPE.COLOR_8_CHANNEL }; private readonly static string _colorProfileResources = "ColorProfiles"; private readonly static string _sRGBProfileName = "sRGB_icm"; [StructLayout(LayoutKind.Sequential)] private struct AbbreviatedPROFILEHEADER { public uint phSize; // profile size in bytes public uint phCMMType; // CMM for this profile public uint phVersion; // profile format version number public uint phClass; // type of profile public NativeMethods.ColorSpace phDataColorSpace; // color space of data public uint phConnectionSpace; // PCS public uint phDateTime_0; // date profile was created public uint phDateTime_1; // date profile was created public uint phDateTime_2; // date profile was created public uint phSignature; // magic number ("Reserved for internal use.") public uint phPlatform; // primary platform public uint phProfileFlags; // various bit settings public uint phManufacturer; // device manufacturer public uint phModel; // device model number public uint phAttributes_0; // device attributes public uint phAttributes_1; // device attributes public uint phRenderingIntent; // rendering intent public uint phIlluminant_0; // profile illuminant public uint phIlluminant_1; // profile illuminant public uint phIlluminant_2; // profile illuminant public uint phCreator; // profile creator // Not including the reserved bits because we don't want to unnecessarily // increase the size of ColorContext // public byte phReserved[44]; }; internal enum StandardColorSpace : int { Unknown = 0, Srgb = 1, ScRgb = 2, Rgb = 3, Cmyk = 4, Gray = 6, Multichannel = 7 } #endregion } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- UnsafeNetInfoNativeMethods.cs
- UpdatePanel.cs
- EntityTypeEmitter.cs
- MatrixAnimationBase.cs
- FrameworkObject.cs
- ClaimSet.cs
- CompensationTokenData.cs
- Vector3D.cs
- TransformConverter.cs
- MetadataArtifactLoaderResource.cs
- ColorContextHelper.cs
- LineServicesRun.cs
- HtmlElementEventArgs.cs
- CompilationUnit.cs
- FileDialog.cs
- ConnectionString.cs
- BrushValueSerializer.cs
- DBCommand.cs
- DropDownList.cs
- XmlQueryCardinality.cs
- WpfKnownTypeInvoker.cs
- FlowLayoutSettings.cs
- XmlnsCompatibleWithAttribute.cs
- ApplicationBuildProvider.cs
- EntitySqlException.cs
- CommandEventArgs.cs
- ListDictionaryInternal.cs
- XmlArrayItemAttribute.cs
- TextParagraphProperties.cs
- AssociationType.cs
- RegularExpressionValidator.cs
- StyleModeStack.cs
- ErrorHandler.cs
- ProjectedWrapper.cs
- KnownTypesProvider.cs
- XmlEntityReference.cs
- SQLInt64.cs
- HostProtectionException.cs
- IncrementalReadDecoders.cs
- TemplateApplicationHelper.cs
- FileDialogCustomPlacesCollection.cs
- WebEvents.cs
- ToolStripPanelSelectionGlyph.cs
- EmptyReadOnlyDictionaryInternal.cs
- SudsWriter.cs
- CompilationLock.cs
- DefaultShape.cs
- StandardCommands.cs
- RenderingEventArgs.cs
- IISMapPath.cs
- PrintPreviewControl.cs
- ConstraintEnumerator.cs
- MsmqUri.cs
- PageAdapter.cs
- ValidationHelper.cs
- TextRangeProviderWrapper.cs
- LoginUtil.cs
- SurrogateSelector.cs
- CommandBinding.cs
- ServiceHost.cs
- ErrorFormatterPage.cs
- TimerElapsedEvenArgs.cs
- DefaultSettingsSection.cs
- AsyncPostBackTrigger.cs
- StandardCommands.cs
- RegexParser.cs
- DataGridTableCollection.cs
- MessageRpc.cs
- RuleInfoComparer.cs
- BoundField.cs
- PathData.cs
- Rect.cs
- AspNetPartialTrustHelpers.cs
- AudioFormatConverter.cs
- FactoryRecord.cs
- StaticSiteMapProvider.cs
- StreamUpdate.cs
- COM2ComponentEditor.cs
- PingOptions.cs
- SelectionRange.cs
- RestrictedTransactionalPackage.cs
- WindowsToolbarItemAsMenuItem.cs
- DoubleAnimation.cs
- ApplicationDirectoryMembershipCondition.cs
- DrawingContext.cs
- UIElementAutomationPeer.cs
- Point4D.cs
- RadioButton.cs
- GlyphRunDrawing.cs
- XmlSchemaSimpleTypeUnion.cs
- PreservationFileWriter.cs
- RawUIStateInputReport.cs
- Panel.cs
- PrintingPermission.cs
- HitTestDrawingContextWalker.cs
- ContainerUIElement3D.cs
- SecurityTokenProviderContainer.cs
- Timer.cs
- DataServiceHost.cs
- KeyToListMap.cs