diff --git a/Directory.Build.targets b/Directory.Build.targets index 78b69b7c99e..13922f84c46 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -16,7 +16,7 @@ - + @@ -87,4 +87,29 @@ + + + + + + + + $(NoWarn);CS0436 + + + + + + + true + + + + + + diff --git a/Winforms.sln b/Winforms.sln index 8fe6a189ef6..dd35fd184a6 100644 --- a/Winforms.sln +++ b/Winforms.sln @@ -155,6 +155,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildAssist", "BuildAssist" EndProject Project("{13B669BE-BB05-4DDF-9536-439F39A36129}") = "BuildAssist", "src\BuildAssist\BuildAssist.msbuildproj", "{20E7C5E8-CD56-4A1C-9CB2-B7169FD7A5B8}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Drawing.Common", "src\System.Drawing.Common\src\System.Drawing.Common.csproj", "{90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Drawing.Common.Tests", "src\System.Drawing.Common\tests\System.Drawing.Common.Tests.csproj", "{7650F24E-7132-42CF-ADCE-830C8DB26EE5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -831,6 +835,38 @@ Global {20E7C5E8-CD56-4A1C-9CB2-B7169FD7A5B8}.Release|x64.Build.0 = Release|Any CPU {20E7C5E8-CD56-4A1C-9CB2-B7169FD7A5B8}.Release|x86.ActiveCfg = Release|Any CPU {20E7C5E8-CD56-4A1C-9CB2-B7169FD7A5B8}.Release|x86.Build.0 = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|arm64.ActiveCfg = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|arm64.Build.0 = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|x64.ActiveCfg = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|x64.Build.0 = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|x86.ActiveCfg = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Debug|x86.Build.0 = Debug|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|Any CPU.Build.0 = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|arm64.ActiveCfg = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|arm64.Build.0 = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|x64.ActiveCfg = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|x64.Build.0 = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|x86.ActiveCfg = Release|Any CPU + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D}.Release|x86.Build.0 = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|arm64.ActiveCfg = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|arm64.Build.0 = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|x64.ActiveCfg = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|x64.Build.0 = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|x86.ActiveCfg = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Debug|x86.Build.0 = Debug|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|Any CPU.Build.0 = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|arm64.ActiveCfg = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|arm64.Build.0 = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|x64.ActiveCfg = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|x64.Build.0 = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|x86.ActiveCfg = Release|Any CPU + {7650F24E-7132-42CF-ADCE-830C8DB26EE5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -888,6 +924,8 @@ Global {93651A99-C9EB-4F83-8A66-DF9D21574550} = {DF68A171-D27B-4E6A-8A7E-63A651622355} {B3CE7FA8-21C5-4CE8-970E-8210D1D89251} = {77FEDB47-F7F6-490D-AF7C-ABB4A9E0B9D7} {20E7C5E8-CD56-4A1C-9CB2-B7169FD7A5B8} = {B3CE7FA8-21C5-4CE8-970E-8210D1D89251} + {90CC3A84-1087-48A0-8FDC-3A0F6D4D3B4D} = {77FEDB47-F7F6-490D-AF7C-ABB4A9E0B9D7} + {7650F24E-7132-42CF-ADCE-830C8DB26EE5} = {583F1292-AE8D-4511-B8D8-A81FE4642DDC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7B1B0433-F612-4E5A-BE7E-FCF5B9F6E136} diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 24b9dbd3fd1..21d47d9a150 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -31,10 +31,6 @@ Note: if the Uri is a new place, you will need to add a subscription from that p https://github.com/dotnet/runtime 541347cbb4270f3861a8bb5eada9f1547d309b3a - - https://github.com/dotnet/runtime - 541347cbb4270f3861a8bb5eada9f1547d309b3a - https://github.com/dotnet/runtime 541347cbb4270f3861a8bb5eada9f1547d309b3a diff --git a/eng/Versions.props b/eng/Versions.props index 990b68544e2..27c37ebb014 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -35,7 +35,6 @@ 8.0.0-preview.3.23162.2 8.0.0-preview.3.23162.2 8.0.0-preview.3.23162.2 - 8.0.0-preview.3.23162.2 8.0.0-preview.3.23162.2 8.0.0-preview.3.23162.2 8.0.0-preview.3.23162.2 @@ -76,6 +75,9 @@ 8.0.0-preview.3.23162.2 4.10.0 + 8.0.0-beta.23107.1 + 8.0.0-beta.23107.1 + 8.0.0-beta.23107.1 14.2.0 diff --git a/eng/pipelines/build.yml b/eng/pipelines/build.yml index e02f8cf6125..df0d43f7ffa 100644 --- a/eng/pipelines/build.yml +++ b/eng/pipelines/build.yml @@ -90,7 +90,7 @@ jobs: $(_OfficialBuildIdArgs) $(_InternalRuntimeDownloadArgs) /p:Coverage=$(_Coverage) - /p:TestRunnerAdditionalArguments='-notrait Category=IgnoreForCI' + /p:TestRunnerAdditionalArguments='-notrait Category=IgnoreForCI -notrait Category=failing' /bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\Test-${{ parameters.targetArchitecture }}.binlog /m:1 displayName: Run Unit Tests @@ -105,7 +105,7 @@ jobs: /p:TargetArchitecture=${{ parameters.targetArchitecture }} $(_OfficialBuildIdArgs) $(_InternalRuntimeDownloadArgs) - /p:TestRunnerAdditionalArguments='-notrait Category=IgnoreForCI' + /p:TestRunnerAdditionalArguments='-notrait Category=IgnoreForCI -notrait Category=failing' /bl:$(BUILD.SOURCESDIRECTORY)\artifacts\log\$(_BuildConfig)\IntegrationTest-${{ parameters.targetArchitecture }}.binlog /m:1 displayName: Run Integration Tests diff --git a/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj b/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj index 58ae9d6ab88..7b6046ee57b 100644 --- a/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj +++ b/pkg/Microsoft.Private.Winforms/Microsoft.Private.Winforms.csproj @@ -76,6 +76,9 @@ + + + diff --git a/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 index 3487e2ebcde..e9d0a0fb188 100644 --- a/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 +++ b/pkg/Microsoft.Private.Winforms/sdk/dotnet-windowsdesktop/UpdateFileClassification.ps1 @@ -28,10 +28,6 @@ $assemblies = $xmlDoc.package.files.file | ` Select-Object -Unique @{Name="Path";Expression={Split-Path $_.target -Leaf}} | ` Select-Object -ExpandProperty Path; -# this isn't explicitly present in the list -$assemblies += 'System.Drawing.Common.dll'; - - $needGenerate = $null; [bool]::TryParse($GenerateManifest, [ref]$needGenerate) | Out-Null; $servicingRelease = $null; diff --git a/src/Common/src/DisableRuntimeMarshalling.cs b/src/Common/src/DisableRuntimeMarshalling.cs new file mode 100644 index 00000000000..9a6bc544e55 --- /dev/null +++ b/src/Common/src/DisableRuntimeMarshalling.cs @@ -0,0 +1,5 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Used to indicate that runtime marshalling should be disabled. +[assembly: System.Runtime.CompilerServices.DisableRuntimeMarshalling] diff --git a/src/Common/src/HandleRefMarshaller.cs b/src/Common/src/HandleRefMarshaller.cs new file mode 100644 index 00000000000..ec541e6a15f --- /dev/null +++ b/src/Common/src/HandleRefMarshaller.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Runtime.InteropServices.Marshalling +{ + [CustomMarshaller(typeof(HandleRef), MarshalMode.ManagedToUnmanagedIn, typeof(KeepAliveMarshaller))] + internal static class HandleRefMarshaller + { + internal struct KeepAliveMarshaller + { + private HandleRef _handle; + + public void FromManaged(HandleRef handle) + { + _handle = handle; + } + + public IntPtr ToUnmanaged() => _handle.Handle; + + public void OnInvoked() => GC.KeepAlive(_handle.Wrapper); + + public void Free() { } + } + } +} diff --git a/src/Common/src/LocalAppContextSwitches.Common.cs b/src/Common/src/LocalAppContextSwitches.Common.cs new file mode 100644 index 00000000000..c04ecff8c6c --- /dev/null +++ b/src/Common/src/LocalAppContextSwitches.Common.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Copied from https://raw.githubusercontent.com/dotnet/runtime/main/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs + +using System.Runtime.CompilerServices; + +namespace System +{ + // Helper method for local caching of compatibility quirks. Keep this lean and simple - this file is included into + // every framework assembly that implements any compatibility quirks. + internal static partial class LocalAppContextSwitches + { + // Returns value of given switch using provided cache. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool GetSwitchValue(string switchName, ref bool switchValue) => + AppContext.TryGetSwitch(switchName, out switchValue); + + // Returns value of given switch using provided cache. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue) + { + // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false + if (cachedSwitchValue < 0) return false; + if (cachedSwitchValue > 0) return true; + + return GetCachedSwitchValueInternal(switchName, ref cachedSwitchValue); + } + + private static bool GetCachedSwitchValueInternal(string switchName, ref int cachedSwitchValue) + { + bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled); + if (!hasSwitch) + { + isSwitchEnabled = GetSwitchDefaultValue(switchName); + } + + AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching); + if (!disableCaching) + { + cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/; + } + + return isSwitchEnabled; + } + + // Provides default values for switches if they're not always false by default + private static bool GetSwitchDefaultValue(string switchName) + { + if (switchName == "Switch.System.Runtime.Serialization.SerializationGuard") + { + return true; + } + + if (switchName == "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization") + { + return true; + } + + return false; + } + } +} diff --git a/src/Common/src/NullableAttributes.cs b/src/Common/src/NullableAttributes.cs new file mode 100644 index 00000000000..361f88fa360 --- /dev/null +++ b/src/Common/src/NullableAttributes.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ +#if !NETSTANDARD2_1 + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } +#endif + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } +} diff --git a/src/Common/src/ObsoleteAttribute.cs b/src/Common/src/ObsoleteAttribute.cs new file mode 100644 index 00000000000..99e3b2054c2 --- /dev/null +++ b/src/Common/src/ObsoleteAttribute.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/*============================================================ +** +** +** +** Purpose: Attribute for functions, etc that will be removed. +** +** +===========================================================*/ + +// Copied from https://raw.githubusercontent.com/dotnet/runtime/main/src/libraries/System.Private.CoreLib/src/System/ObsoleteAttribute.cs + +namespace System +{ + // This attribute is attached to members that are not to be used any longer. + // Message is some human readable explanation of what to use + // Error indicates if the compiler should treat usage of such a method as an + // error. (this would be used if the actual implementation of the obsolete + // method's implementation had changed). + // DiagnosticId. Represents the ID the compiler will use when reporting a use of the API. + // UrlFormat.The URL that should be used by an IDE for navigating to corresponding documentation. Instead of taking the URL directly, + // the API takes a format string. This allows having a generic URL that includes the diagnostic ID. + // + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | + AttributeTargets.Interface | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Delegate, + Inherited = false)] + internal sealed class ObsoleteAttribute : Attribute + { + public ObsoleteAttribute() + { + } + + public ObsoleteAttribute(string? message) + { + Message = message; + } + + public ObsoleteAttribute(string? message, bool error) + { + Message = message; + IsError = error; + } + + public string? Message { get; } + + public bool IsError { get; } + + public string? DiagnosticId { get; set; } + + public string? UrlFormat { get; set; } + } +} diff --git a/src/Common/src/PlatformAttributes.cs b/src/Common/src/PlatformAttributes.cs new file mode 100644 index 00000000000..06359cf9117 --- /dev/null +++ b/src/Common/src/PlatformAttributes.cs @@ -0,0 +1,199 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Runtime.Versioning +{ + /// + /// Base type for all platform-specific API attributes. + /// +#pragma warning disable CS3015 // Type has no accessible constructors which use only CLS-compliant types +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + abstract class OSPlatformAttribute : Attribute +#pragma warning restore CS3015 + { + private protected OSPlatformAttribute(string platformName) + { + PlatformName = platformName; + } + public string PlatformName { get; } + } + + /// + /// Records the platform that the project targeted. + /// + [AttributeUsage(AttributeTargets.Assembly, + AllowMultiple = false, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class TargetPlatformAttribute : OSPlatformAttribute + { + public TargetPlatformAttribute(string platformName) : base(platformName) + { + } + } + + /// + /// Records the operating system (and minimum version) that supports an API. Multiple attributes can be + /// applied to indicate support on multiple operating systems. + /// + /// + /// Callers can apply a + /// or use guards to prevent calls to APIs on unsupported operating systems. + /// + /// A given platform should only be specified once. + /// + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class SupportedOSPlatformAttribute : OSPlatformAttribute + { + public SupportedOSPlatformAttribute(string platformName) : base(platformName) + { + } + } + + /// + /// Marks APIs that were removed in a given operating system version. + /// + /// + /// Primarily used by OS bindings to indicate APIs that are only available in + /// earlier versions. + /// + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class UnsupportedOSPlatformAttribute : OSPlatformAttribute + { + public UnsupportedOSPlatformAttribute(string platformName) : base(platformName) + { + } + public UnsupportedOSPlatformAttribute(string platformName, string? message) : base(platformName) + { + Message = message; + } + public string? Message { get; } + } + + /// + /// Marks APIs that were obsoleted in a given operating system version. + /// + /// + /// Primarily used by OS bindings to indicate APIs that should not be used anymore. + /// + [AttributeUsage(AttributeTargets.Assembly | + AttributeTargets.Class | + AttributeTargets.Constructor | + AttributeTargets.Enum | + AttributeTargets.Event | + AttributeTargets.Field | + AttributeTargets.Interface | + AttributeTargets.Method | + AttributeTargets.Module | + AttributeTargets.Property | + AttributeTargets.Struct, + AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class ObsoletedOSPlatformAttribute : OSPlatformAttribute + { + public ObsoletedOSPlatformAttribute(string platformName) : base(platformName) + { + } + public ObsoletedOSPlatformAttribute(string platformName, string? message) : base(platformName) + { + Message = message; + } + public string? Message { get; } + public string? Url { get; set; } + } + + /// + /// Annotates a custom guard field, property or method with a supported platform name and optional version. + /// Multiple attributes can be applied to indicate guard for multiple supported platforms. + /// + /// + /// Callers can apply a to a field, property or method + /// and use that field, property or method in a conditional or assert statements in order to safely call platform specific APIs. + /// + /// The type of the field or property should be boolean, the method return type should be boolean in order to be used as platform guard. + /// + [AttributeUsage(AttributeTargets.Field | + AttributeTargets.Method | + AttributeTargets.Property, + AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class SupportedOSPlatformGuardAttribute : OSPlatformAttribute + { + public SupportedOSPlatformGuardAttribute(string platformName) : base(platformName) + { + } + } + + /// + /// Annotates the custom guard field, property or method with an unsupported platform name and optional version. + /// Multiple attributes can be applied to indicate guard for multiple unsupported platforms. + /// + /// + /// Callers can apply a to a field, property or method + /// and use that field, property or method in a conditional or assert statements as a guard to safely call APIs unsupported on those platforms. + /// + /// The type of the field or property should be boolean, the method return type should be boolean in order to be used as platform guard. + /// + [AttributeUsage(AttributeTargets.Field | + AttributeTargets.Method | + AttributeTargets.Property, + AllowMultiple = true, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class UnsupportedOSPlatformGuardAttribute : OSPlatformAttribute + { + public UnsupportedOSPlatformGuardAttribute(string platformName) : base(platformName) + { + } + } +} diff --git a/src/Common/src/RequiresUnreferencedCodeAttribute.cs b/src/Common/src/RequiresUnreferencedCodeAttribute.cs new file mode 100644 index 00000000000..12688c1075a --- /dev/null +++ b/src/Common/src/RequiresUnreferencedCodeAttribute.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Indicates that the specified method requires dynamic access to code that is not referenced + /// statically, for example through . + /// + /// + /// This allows tools to understand which methods are unsafe to call when removing unreferenced + /// code from an application. + /// + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class RequiresUnreferencedCodeAttribute : Attribute + { + /// + /// Initializes a new instance of the class + /// with the specified message. + /// + /// + /// A message that contains information about the usage of unreferenced code. + /// + public RequiresUnreferencedCodeAttribute(string message) + { + Message = message; + } + + /// + /// Gets a message that contains information about the usage of unreferenced code. + /// + public string Message { get; } + + /// + /// Gets or sets an optional URL that contains more information about the method, + /// why it requires unreferenced code, and what options a consumer has to deal with it. + /// + public string? Url { get; set; } + } +} diff --git a/src/Common/src/SR.cs b/src/Common/src/SR.cs new file mode 100644 index 00000000000..96fcadafb5c --- /dev/null +++ b/src/Common/src/SR.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Resources; + +namespace System +{ + internal static partial class SR + { + private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out bool usingResourceKeys) ? usingResourceKeys : false; + + // This method is used to decide if we need to append the exception message parameters to the message when calling SR.Format. + // by default it returns the value of System.Resources.UseSystemResourceKeys AppContext switch or false if not specified. + // Native code generators can replace the value this returns based on user input at the time of native code generation. + // The Linker is also capable of replacing the value of this method when the application is being trimmed. + internal static bool UsingResourceKeys() => s_usingResourceKeys; + + internal static string GetResourceString(string resourceKey) + { + if (UsingResourceKeys()) + { + return resourceKey; + } + + string? resourceString = null; + try + { + resourceString = +#if SYSTEM_PRIVATE_CORELIB || NATIVEAOT + InternalGetResourceString(resourceKey); +#else + ResourceManager.GetString(resourceKey); +#endif + } + catch (MissingManifestResourceException) { } + + return resourceString!; // only null if missing resources + } + + internal static string GetResourceString(string resourceKey, string defaultString) + { + string resourceString = GetResourceString(resourceKey); + + return resourceKey == resourceString || resourceString == null ? defaultString : resourceString; + } + + internal static string Format(string resourceFormat, object? p1) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1); + } + + return string.Format(resourceFormat, p1); + } + + internal static string Format(string resourceFormat, object? p1, object? p2) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2); + } + + return string.Format(resourceFormat, p1, p2); + } + + internal static string Format(string resourceFormat, object? p1, object? p2, object? p3) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2, p3); + } + + return string.Format(resourceFormat, p1, p2, p3); + } + + internal static string Format(string resourceFormat, params object?[]? args) + { + if (args != null) + { + if (UsingResourceKeys()) + { + return resourceFormat + ", " + string.Join(", ", args); + } + + return string.Format(resourceFormat, args); + } + + return resourceFormat; + } + + internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1); + } + + return string.Format(provider, resourceFormat, p1); + } + + internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2); + } + + return string.Format(provider, resourceFormat, p1, p2); + } + + internal static string Format(IFormatProvider? provider, string resourceFormat, object? p1, object? p2, object? p3) + { + if (UsingResourceKeys()) + { + return string.Join(", ", resourceFormat, p1, p2, p3); + } + + return string.Format(provider, resourceFormat, p1, p2, p3); + } + + internal static string Format(IFormatProvider? provider, string resourceFormat, params object?[]? args) + { + if (args != null) + { + if (UsingResourceKeys()) + { + return resourceFormat + ", " + string.Join(", ", args); + } + + return string.Format(provider, resourceFormat, args); + } + + return resourceFormat; + } + } +} diff --git a/src/Common/src/UnconditionalSuppressMessageAttribute.cs b/src/Common/src/UnconditionalSuppressMessageAttribute.cs new file mode 100644 index 00000000000..2d82ed0c0e7 --- /dev/null +++ b/src/Common/src/UnconditionalSuppressMessageAttribute.cs @@ -0,0 +1,90 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Diagnostics.CodeAnalysis +{ + /// + /// Suppresses reporting of a specific rule violation, allowing multiple suppressions on a + /// single code artifact. + /// + /// + /// is different than + /// in that it doesn't have a + /// . So it is always preserved in the compiled assembly. + /// + [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)] +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class UnconditionalSuppressMessageAttribute : Attribute + { + /// + /// Initializes a new instance of the + /// class, specifying the category of the tool and the identifier for an analysis rule. + /// + /// The category for the attribute. + /// The identifier of the analysis rule the attribute applies to. + public UnconditionalSuppressMessageAttribute(string category, string checkId) + { + Category = category; + CheckId = checkId; + } + + /// + /// Gets the category identifying the classification of the attribute. + /// + /// + /// The property describes the tool or tool analysis category + /// for which a message suppression attribute applies. + /// + public string Category { get; } + + /// + /// Gets the identifier of the analysis tool rule to be suppressed. + /// + /// + /// Concatenated together, the and + /// properties form a unique check identifier. + /// + public string CheckId { get; } + + /// + /// Gets or sets the scope of the code that is relevant for the attribute. + /// + /// + /// The Scope property is an optional argument that specifies the metadata scope for which + /// the attribute is relevant. + /// + public string? Scope { get; set; } + + /// + /// Gets or sets a fully qualified path that represents the target of the attribute. + /// + /// + /// The property is an optional argument identifying the analysis target + /// of the attribute. An example value is "System.IO.Stream.ctor():System.Void". + /// Because it is fully qualified, it can be long, particularly for targets such as parameters. + /// The analysis tool user interface should be capable of automatically formatting the parameter. + /// + public string? Target { get; set; } + + /// + /// Gets or sets an optional argument expanding on exclusion criteria. + /// + /// + /// The property is an optional argument that specifies additional + /// exclusion where the literal metadata target is not sufficiently precise. For example, + /// the cannot be applied within a method, + /// and it may be desirable to suppress a violation against a statement in the method that will + /// give a rule violation, but not against all statements in the method. + /// + public string? MessageId { get; set; } + + /// + /// Gets or sets the justification for suppressing the code analysis message. + /// + public string? Justification { get; set; } + } +} diff --git a/src/Common/src/ValueStringBuilder.cs b/src/Common/src/ValueStringBuilder.cs new file mode 100644 index 00000000000..4ebb59692e3 --- /dev/null +++ b/src/Common/src/ValueStringBuilder.cs @@ -0,0 +1,332 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Copied from https://raw.githubusercontent.com/dotnet/runtime/main/src/libraries/Common/src/System/Text/ValueStringBuilder.cs + +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Text +{ + internal ref partial struct ValueStringBuilder + { + private char[]? _arrayToReturnToPool; + private Span _chars; + private int _pos; + + public ValueStringBuilder(Span initialBuffer) + { + _arrayToReturnToPool = null; + _chars = initialBuffer; + _pos = 0; + } + + public ValueStringBuilder(int initialCapacity) + { + _arrayToReturnToPool = ArrayPool.Shared.Rent(initialCapacity); + _chars = _arrayToReturnToPool; + _pos = 0; + } + + public int Length + { + get => _pos; + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _chars.Length); + _pos = value; + } + } + + public int Capacity => _chars.Length; + + public void EnsureCapacity(int capacity) + { + // This is not expected to be called this with negative capacity + Debug.Assert(capacity >= 0); + + // If the caller has a bug and calls this with negative capacity, make sure to call Grow to throw an exception. + if ((uint)capacity > (uint)_chars.Length) + Grow(capacity - _pos); + } + + /// + /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" + /// + public ref char GetPinnableReference() + { + return ref MemoryMarshal.GetReference(_chars); + } + + /// + /// Get a pinnable reference to the builder. + /// + /// Ensures that the builder has a null char after + public ref char GetPinnableReference(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return ref MemoryMarshal.GetReference(_chars); + } + + public ref char this[int index] + { + get + { + Debug.Assert(index < _pos); + return ref _chars[index]; + } + } + + public override string ToString() + { + string s = _chars.Slice(0, _pos).ToString(); + Dispose(); + return s; + } + + /// Returns the underlying storage of the builder. + public Span RawChars => _chars; + + /// + /// Returns a span around the contents of the builder. + /// + /// Ensures that the builder has a null char after + public ReadOnlySpan AsSpan(bool terminate) + { + if (terminate) + { + EnsureCapacity(Length + 1); + _chars[Length] = '\0'; + } + return _chars.Slice(0, _pos); + } + + public ReadOnlySpan AsSpan() => _chars.Slice(0, _pos); + public ReadOnlySpan AsSpan(int start) => _chars.Slice(start, _pos - start); + public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length); + + public bool TryCopyTo(Span destination, out int charsWritten) + { + if (_chars.Slice(0, _pos).TryCopyTo(destination)) + { + charsWritten = _pos; + Dispose(); + return true; + } + else + { + charsWritten = 0; + Dispose(); + return false; + } + } + + public void Insert(int index, char value, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + _chars.Slice(index, count).Fill(value); + _pos += count; + } + + public void Insert(int index, string? s) + { + if (s == null) + { + return; + } + + int count = s.Length; + + if (_pos > (_chars.Length - count)) + { + Grow(count); + } + + int remaining = _pos - index; + _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); + s +#if !NETCOREAPP + .AsSpan() +#endif + .CopyTo(_chars.Slice(index)); + _pos += count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(char c) + { + int pos = _pos; + if ((uint)pos < (uint)_chars.Length) + { + _chars[pos] = c; + _pos = pos + 1; + } + else + { + GrowAndAppend(c); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Append(string? s) + { + if (s == null) + { + return; + } + + int pos = _pos; + if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc. + { + _chars[pos] = s[0]; + _pos = pos + 1; + } + else + { + AppendSlow(s); + } + } + + private void AppendSlow(string s) + { + int pos = _pos; + if (pos > _chars.Length - s.Length) + { + Grow(s.Length); + } + + s +#if !NETCOREAPP + .AsSpan() +#endif + .CopyTo(_chars.Slice(pos)); + _pos += s.Length; + } + + public void Append(char c, int count) + { + if (_pos > _chars.Length - count) + { + Grow(count); + } + + Span dst = _chars.Slice(_pos, count); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = c; + } + _pos += count; + } + + public unsafe void Append(char* value, int length) + { + int pos = _pos; + if (pos > _chars.Length - length) + { + Grow(length); + } + + Span dst = _chars.Slice(_pos, length); + for (int i = 0; i < dst.Length; i++) + { + dst[i] = *value++; + } + _pos += length; + } + + public void Append(ReadOnlySpan value) + { + int pos = _pos; + if (pos > _chars.Length - value.Length) + { + Grow(value.Length); + } + + value.CopyTo(_chars.Slice(_pos)); + _pos += value.Length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span AppendSpan(int length) + { + int origPos = _pos; + if (origPos > _chars.Length - length) + { + Grow(length); + } + + _pos = origPos + length; + return _chars.Slice(origPos, length); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowAndAppend(char c) + { + Grow(1); + Append(c); + } + + /// + /// Resize the internal buffer either by doubling current buffer size or + /// by adding to + /// whichever is greater. + /// + /// + /// Number of chars requested beyond current position. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void Grow(int additionalCapacityBeyondPos) + { + Debug.Assert(additionalCapacityBeyondPos > 0); + Debug.Assert(_pos > _chars.Length - additionalCapacityBeyondPos, "Grow called incorrectly, no resize is needed."); + + const uint ArrayMaxLength = 0x7FFFFFC7; // same as Array.MaxLength + + // Increase to at least the required size (_pos + additionalCapacityBeyondPos), but try + // to double the size if possible, bounding the doubling to not go beyond the max array length. + int newCapacity = (int)Math.Max( + (uint)(_pos + additionalCapacityBeyondPos), + Math.Min((uint)_chars.Length * 2, ArrayMaxLength)); + + // Make sure to let Rent throw an exception if the caller has a bug and the desired capacity is negative. + // This could also go negative if the actual required length wraps around. + char[] poolArray = ArrayPool.Shared.Rent(newCapacity); + + _chars.Slice(0, _pos).CopyTo(poolArray); + + char[]? toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = poolArray; + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + char[]? toReturn = _arrayToReturnToPool; + this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again + if (toReturn != null) + { + ArrayPool.Shared.Return(toReturn); + } + } + } +} diff --git a/src/Common/tests/TestUtilities/DebuggerAttributes.cs b/src/Common/tests/TestUtilities/DebuggerAttributes.cs new file mode 100644 index 00000000000..4b352651099 --- /dev/null +++ b/src/Common/tests/TestUtilities/DebuggerAttributes.cs @@ -0,0 +1,250 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable disable + +using System.Reflection; +using System.Text; + +namespace System.Diagnostics +{ + internal class DebuggerAttributeInfo + { + public object Instance { get; set; } + public IEnumerable Properties { get; set; } + } + + internal static class DebuggerAttributes + { + internal static object GetFieldValue(object obj, string fieldName) + { + return GetField(obj, fieldName).GetValue(obj); + } + + internal static void InvokeDebuggerTypeProxyProperties(object obj) + { + DebuggerAttributeInfo info = ValidateDebuggerTypeProxyProperties(obj.GetType(), obj); + foreach (PropertyInfo pi in info.Properties) + { + pi.GetValue(info.Instance, null); + } + } + + internal static DebuggerAttributeInfo ValidateDebuggerTypeProxyProperties(object obj) + { + return ValidateDebuggerTypeProxyProperties(obj.GetType(), obj); + } + + internal static DebuggerAttributeInfo ValidateDebuggerTypeProxyProperties(Type type, object obj) + { + return ValidateDebuggerTypeProxyProperties(type, type.GenericTypeArguments, obj); + } + + internal static DebuggerAttributeInfo ValidateDebuggerTypeProxyProperties(Type type, Type[] genericTypeArguments, object obj) + { + Type proxyType = GetProxyType(type, genericTypeArguments); + + // Create an instance of the proxy type, and make sure we can access all of the instance properties + // on the type without exception + object proxyInstance = Activator.CreateInstance(proxyType, obj); + IEnumerable properties = GetDebuggerVisibleProperties(proxyType); + return new DebuggerAttributeInfo + { + Instance = proxyInstance, + Properties = properties + }; + } + + public static DebuggerBrowsableState? GetDebuggerBrowsableState(MemberInfo info) + { + CustomAttributeData debuggerBrowsableAttribute = info.CustomAttributes + .SingleOrDefault(a => a.AttributeType == typeof(DebuggerBrowsableAttribute)); + // Enums in attribute constructors are boxed as ints, so cast to int? first. + return (DebuggerBrowsableState?)(int?)debuggerBrowsableAttribute?.ConstructorArguments.Single().Value; + } + + public static IEnumerable GetDebuggerVisibleFields(Type debuggerAttributeType) + { + // The debugger doesn't evaluate non-public members of type proxies. + IEnumerable visibleFields = debuggerAttributeType.GetFields() + .Where(fi => fi.IsPublic && GetDebuggerBrowsableState(fi) != DebuggerBrowsableState.Never); + return visibleFields; + } + + public static IEnumerable GetDebuggerVisibleProperties(Type debuggerAttributeType) + { + // The debugger doesn't evaluate non-public members of type proxies. GetGetMethod returns null if the getter is non-public. + IEnumerable visibleProperties = debuggerAttributeType.GetProperties() + .Where(pi => pi.GetGetMethod() is object && GetDebuggerBrowsableState(pi) != DebuggerBrowsableState.Never); + return visibleProperties; + } + + public static object GetProxyObject(object obj) => Activator.CreateInstance(GetProxyType(obj), obj); + + public static Type GetProxyType(object obj) => GetProxyType(obj.GetType()); + + public static Type GetProxyType(Type type) => GetProxyType(type, type.GenericTypeArguments); + + private static Type GetProxyType(Type type, Type[] genericTypeArguments) + { + // Get the DebuggerTypeProxyAttribute for obj + CustomAttributeData[] attrs = + type.GetTypeInfo().CustomAttributes + .Where(a => a.AttributeType == typeof(DebuggerTypeProxyAttribute)) + .ToArray(); + if (attrs.Length != 1) + { + throw new InvalidOperationException($"Expected one DebuggerTypeProxyAttribute on {type}."); + } + + CustomAttributeData cad = attrs[0]; + + Type proxyType = cad.ConstructorArguments[0].ArgumentType == typeof(Type) ? + (Type)cad.ConstructorArguments[0].Value : + Type.GetType((string)cad.ConstructorArguments[0].Value); + if (genericTypeArguments.Length > 0) + { + proxyType = proxyType.MakeGenericType(genericTypeArguments); + } + + return proxyType; + } + + internal static string ValidateDebuggerDisplayReferences(object obj) + { + // Get the DebuggerDisplayAttribute for obj + Type objType = obj.GetType(); + CustomAttributeData[] attrs = + objType.GetTypeInfo().CustomAttributes + .Where(a => a.AttributeType == typeof(DebuggerDisplayAttribute)) + .ToArray(); + if (attrs.Length != 1) + { + throw new InvalidOperationException($"Expected one DebuggerDisplayAttribute on {objType}."); + } + + CustomAttributeData cad = attrs[0]; + + // Get the text of the DebuggerDisplayAttribute + string attrText = (string)cad.ConstructorArguments[0].Value; + + string[] segments = attrText.Split(new[] { '{', '}' }); + + if (segments.Length % 2 == 0) + { + throw new InvalidOperationException($"The DebuggerDisplayAttribute for {objType} lacks a closing brace."); + } + + if (segments.Length == 1) + { + throw new InvalidOperationException($"The DebuggerDisplayAttribute for {objType} doesn't reference any expressions."); + } + + var sb = new StringBuilder(); + + for (int i = 0; i < segments.Length; i += 2) + { + string literal = segments[i]; + sb.Append(literal); + + if (i + 1 < segments.Length) + { + string reference = segments[i + 1]; + bool noQuotes = reference.EndsWith(",nq"); + + reference = reference.Replace(",nq", string.Empty); + + // Evaluate the reference. + object member; + if (!TryEvaluateReference(obj, reference, out member)) + { + throw new InvalidOperationException($"The DebuggerDisplayAttribute for {objType} contains the expression \"{reference}\"."); + } + + string memberString = GetDebuggerMemberString(member, noQuotes); + + sb.Append(memberString); + } + } + + return sb.ToString(); + } + + private static string GetDebuggerMemberString(object member, bool noQuotes) + { + string memberString = "null"; + if (member is object) + { + memberString = member.ToString(); + if (member is string) + { + if (!noQuotes) + { + memberString = '"' + memberString + '"'; + } + } + else if (!IsPrimitiveType(member)) + { + memberString = '{' + memberString + '}'; + } + } + + return memberString; + } + + private static bool IsPrimitiveType(object obj) => + obj is byte || obj is sbyte || + obj is short || obj is ushort || + obj is int || obj is uint || + obj is long || obj is ulong || + obj is float || obj is double; + + private static bool TryEvaluateReference(object obj, string reference, out object member) + { + PropertyInfo pi = GetProperty(obj, reference); + if (pi is object) + { + member = pi.GetValue(obj); + return true; + } + + FieldInfo fi = GetField(obj, reference); + if (fi is object) + { + member = fi.GetValue(obj); + return true; + } + + member = null; + return false; + } + + private static FieldInfo GetField(object obj, string fieldName) + { + for (Type t = obj.GetType(); t is object; t = t.GetTypeInfo().BaseType) + { + FieldInfo fi = t.GetTypeInfo().GetDeclaredField(fieldName); + if (fi is object) + { + return fi; + } + } + + return null; + } + + private static PropertyInfo GetProperty(object obj, string propertyName) + { + for (Type t = obj.GetType(); t is object; t = t.GetTypeInfo().BaseType) + { + PropertyInfo pi = t.GetTypeInfo().GetDeclaredProperty(propertyName); + if (pi is object) + { + return pi; + } + } + + return null; + } + } +} diff --git a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileCleanupTestBase.cs b/src/Common/tests/TestUtilities/FileCleanupTestBase.cs similarity index 79% rename from src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileCleanupTestBase.cs rename to src/Common/tests/TestUtilities/FileCleanupTestBase.cs index cef04e3e0f8..8317ccc5645 100644 --- a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileCleanupTestBase.cs +++ b/src/Common/tests/TestUtilities/FileCleanupTestBase.cs @@ -2,11 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Microsoft.VisualBasic.Tests +namespace System { public abstract class FileCleanupTestBase : IDisposable { - internal readonly string TestDirectory; + public readonly string TestDirectory; protected FileCleanupTestBase() { @@ -36,9 +36,9 @@ private void Dispose(bool disposing) } } - internal string GetTestFilePath() => Path.Combine(TestDirectory, GetTestFileName()); + public string GetTestFilePath() => Path.Combine(TestDirectory, GetTestFileName()); - internal string GetTestFileName() => GetUniqueName(); + public string GetTestFileName() => GetUniqueName(); private static string GetUniqueName() => Guid.NewGuid().ToString("D"); } diff --git a/src/Common/tests/TestUtilities/ThreadCultureChange.cs b/src/Common/tests/TestUtilities/ThreadCultureChange.cs new file mode 100644 index 00000000000..d3152716588 --- /dev/null +++ b/src/Common/tests/TestUtilities/ThreadCultureChange.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System +{ + public sealed class ThreadCultureChange : IDisposable + { + private readonly CultureInfo _origCulture = CultureInfo.CurrentCulture; + private readonly CultureInfo _origUICulture = CultureInfo.CurrentUICulture; + + public ThreadCultureChange(string? cultureName) : + this(cultureName is object ? new CultureInfo(cultureName) : null) + { + } + + public ThreadCultureChange(CultureInfo? newCulture) : + this(newCulture, null) + { + } + + public ThreadCultureChange(CultureInfo? newCulture, CultureInfo? newUICulture) + { + if (newCulture is object) + { + _origCulture = CultureInfo.CurrentCulture; + CultureInfo.CurrentCulture = newCulture; + } + + if (newUICulture is object) + { + _origUICulture = CultureInfo.CurrentUICulture; + CultureInfo.CurrentUICulture = newUICulture; + } + } + + public void Dispose() + { + CultureInfo.CurrentCulture = _origCulture; + CultureInfo.CurrentUICulture = _origUICulture; + } + } +} diff --git a/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj b/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj index c38d6455646..e57cd2e7598 100644 --- a/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj +++ b/src/Microsoft.VisualBasic.Forms/src/Microsoft.VisualBasic.Forms.vbproj @@ -45,7 +45,7 @@ - + AssignProjectConfiguration;$(GetCopyToOutputDirectoryItemsDependsOn) diff --git a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/FileLogTraceListenerTests.cs b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/FileLogTraceListenerTests.cs index af696dbc7b3..f25bb80715c 100644 --- a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/FileLogTraceListenerTests.cs +++ b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/FileLogTraceListenerTests.cs @@ -7,7 +7,7 @@ namespace Microsoft.VisualBasic.Logging.Tests { - public class FileLogTraceListenerTests : Microsoft.VisualBasic.Tests.FileCleanupTestBase + public class FileLogTraceListenerTests : FileCleanupTestBase { [Fact] public void Properties() diff --git a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/LogTests.cs b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/LogTests.cs index 884abe9cbe2..da222a56c42 100644 --- a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/LogTests.cs +++ b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/Logging/LogTests.cs @@ -6,7 +6,7 @@ namespace Microsoft.VisualBasic.Logging.Tests { - public class LogTests : Microsoft.VisualBasic.Tests.FileCleanupTestBase + public class LogTests : FileCleanupTestBase { [Fact] public void Properties() diff --git a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileSystemProxyTests.cs b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileSystemProxyTests.cs index 75eece654a4..c87d85bada3 100644 --- a/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileSystemProxyTests.cs +++ b/src/Microsoft.VisualBasic/tests/UnitTests/Microsoft/VisualBasic/MyServices/FileSystemProxyTests.cs @@ -9,7 +9,7 @@ namespace Microsoft.VisualBasic.MyServices.Tests { // File tests cloned from Microsoft.VisualBasic.FileIO.Tests.FileSystemTests. - public class FileSystemProxyTests : Microsoft.VisualBasic.Tests.FileCleanupTestBase + public class FileSystemProxyTests : FileCleanupTestBase { private const string DestData = "xXy"; private const string SourceData = "aAb"; diff --git a/src/System.Drawing.Common/Directory.Build.props b/src/System.Drawing.Common/Directory.Build.props new file mode 100644 index 00000000000..23e90fb22c0 --- /dev/null +++ b/src/System.Drawing.Common/Directory.Build.props @@ -0,0 +1,12 @@ + + + + Open + windows + false + false + + $(NoWarn);CSIsNull001;CSIsNull002;SA1500;SA1513;CA1812;IDE0005;SA1129;SA1408;SA1507;SA1508;CA2229 + + \ No newline at end of file diff --git a/src/System.Drawing.Common/README.md b/src/System.Drawing.Common/README.md new file mode 100644 index 00000000000..08e253582f2 --- /dev/null +++ b/src/System.Drawing.Common/README.md @@ -0,0 +1,16 @@ +# System.Drawing.Common +This assembly provides access to GDI+ basic graphics functionality via types such as [`Bitmap`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap) and [`Font`](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.font). + +Note that `System.Drawing.Common` is only supported on Windows: https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/system-drawing-common-windows-only. + +Documentation can be found at https://learn.microsoft.com/en-us/dotnet/api/system.drawing. + +## Contribution Bar +- [x] [We consider new features, new APIs and performance changes](../README.md#primary-bar) +- [x] [We consider PRs that target this library for new source code analyzers](../README.md#secondary-bars) +- [ ] [We don't accept refactoring changes due to new language features](../README.md#secondary-bars) + +See the [Help Wanted](https://github.com/dotnet/runtime/issues?q=is%3Aissue+is%3Aopen+label%3Aarea-System.Drawing+label%3A%22help+wanted%22) issues. + +## Deployment +`System.Drawing.Common` is provided as a [NuGet package](https://www.nuget.org/packages/System.Drawing.Common) and part of the `Microsoft.WindowsDesktop.App` shared framework. diff --git a/src/System.Drawing.Common/src/CompatibilitySuppressions.xml b/src/System.Drawing.Common/src/CompatibilitySuppressions.xml new file mode 100644 index 00000000000..bdafcf63cc3 --- /dev/null +++ b/src/System.Drawing.Common/src/CompatibilitySuppressions.xml @@ -0,0 +1,191 @@ + + + + + CP0001 + T:System.Drawing.FontConverter + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0001 + T:System.Drawing.IconConverter + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0001 + T:System.Drawing.ImageConverter + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0001 + T:System.Drawing.ImageFormatConverter + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0001 + T:System.Drawing.Printing.MarginsConverter + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0002 + M:System.Drawing.CharacterRange.Equals(System.Drawing.CharacterRange) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.#ctor(System.Numerics.Matrix3x2) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.get_MatrixElements + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Drawing2D.Matrix.set_MatrixElements(System.Numerics.Matrix3x2) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.DrawRectangle(System.Drawing.Pen,System.Drawing.RectangleF) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.FillPie(System.Drawing.Brush,System.Drawing.RectangleF,System.Single,System.Single) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.get_TransformElements + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@,System.Drawing.Region@) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.GetContextInfo(System.Drawing.PointF@) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Graphics.set_TransformElements(System.Numerics.Matrix3x2) + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Imaging.ImageFormat.get_Heif + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + M:System.Drawing.Imaging.ImageFormat.get_Webp + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0002 + F:System.Drawing.Imaging.Encoder.ColorSpace + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0002 + F:System.Drawing.Imaging.Encoder.ImageItems + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0002 + F:System.Drawing.Imaging.Encoder.SaveAsCmyk + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0002 + F:System.Drawing.Imaging.EncoderParameterValueType.ValueTypePointer + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0008 + T:System.Drawing.CharacterRange + lib/net6.0/System.Drawing.Common.dll + lib/netstandard2.0/System.Drawing.Common.dll + true + + + CP0015 + P:System.Drawing.Font.Name:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.Bitmap:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.ContentAlignment:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.Font:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.Icon:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.Image:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + + CP0015 + T:System.Drawing.Imaging.Metafile:[T:System.ComponentModel.EditorAttribute] + lib/netstandard2.0/System.Drawing.Common.dll + lib/net462/System.Drawing.Common.dll + + \ No newline at end of file diff --git a/src/System.Drawing.Common/src/Forwards.cs b/src/System.Drawing.Common/src/Forwards.cs new file mode 100644 index 00000000000..8178458fc67 --- /dev/null +++ b/src/System.Drawing.Common/src/Forwards.cs @@ -0,0 +1,162 @@ +#pragma warning disable CS0618,CA2252 + +#if NETCOREAPP +// This is required for back-compatibility with legacy Xamarin which had these types in System.Drawing.Common.dll +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Color))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.KnownColor))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Point))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.PointF))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Rectangle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.RectangleF))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Size))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SizeF))] +#endif + +#if NETCOREAPP || NETFRAMEWORK +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.ColorTranslator))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SystemColors))] +#endif + +#if NETFRAMEWORK +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Bitmap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.BitmapSuffixInSameAssemblyAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.BitmapSuffixInSatelliteAssemblyAttribute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Brush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Brushes))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.BufferedGraphics))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.BufferedGraphicsContext))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.BufferedGraphicsManager))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.CharacterRange))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.ContentAlignment))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.CopyPixelOperation))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Design.CategoryNameCollection))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.AdjustableArrowCap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.Blend))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.ColorBlend))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.CombineMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.CompositingMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.CompositingQuality))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.CoordinateSpace))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.CustomLineCap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.DashCap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.DashStyle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.FillMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.FlushIntention))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.GraphicsContainer))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.GraphicsPath))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.GraphicsPathIterator))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.GraphicsState))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.HatchBrush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.HatchStyle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.InterpolationMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.LinearGradientBrush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.LinearGradientMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.LineCap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.LineJoin))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.Matrix))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.MatrixOrder))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PathData))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PathGradientBrush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PathPointType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PenAlignment))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PenType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.PixelOffsetMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.QualityMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.RegionData))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.SmoothingMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.WarpMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Drawing2D.WrapMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Font))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.FontFamily))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.FontStyle))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Graphics))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.GraphicsUnit))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Icon))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.IDeviceContext))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Image))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.ImageAnimator))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.BitmapData))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorAdjustType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorChannelFlag))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorMap))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorMapType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorMatrix))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorMatrixFlag))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ColorPalette))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EmfPlusRecordType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EmfType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.Encoder))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EncoderParameter))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EncoderParameters))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EncoderParameterValueType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.EncoderValue))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.FrameDimension))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageAttributes))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageCodecFlags))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageCodecInfo))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageFlags))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageFormat))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.ImageLockMode))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.Metafile))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.MetafileFrameUnit))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.MetafileHeader))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.MetafileType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.MetaHeader))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.PaletteFlags))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.PixelFormat))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.PlayRecordCallback))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.PropertyItem))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Imaging.WmfPlaceableFileHeader))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Pen))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Pens))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.Duplex))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.InvalidPrinterException))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.Margins))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PageSettings))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PaperKind))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PaperSize))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PaperSource))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PaperSourceKind))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PreviewPageInfo))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PreviewPrintController))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintAction))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintController))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintDocument))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrinterResolution))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrinterResolutionKind))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrinterSettings))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrinterUnit))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrinterUnitConvert))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintEventArgs))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintEventHandler))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintPageEventArgs))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintPageEventHandler))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.PrintRange))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.QueryPageSettingsEventArgs))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.QueryPageSettingsEventHandler))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Printing.StandardPrintController))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Region))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.RotateFlipType))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SolidBrush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringAlignment))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringDigitSubstitute))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringFormat))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringFormatFlags))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringTrimming))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.StringUnit))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SystemBrushes))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SystemFonts))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SystemIcons))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.SystemPens))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.FontCollection))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.GenericFontFamilies))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.HotkeyPrefix))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.InstalledFontCollection))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.PrivateFontCollection))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.Text.TextRenderingHint))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.TextureBrush))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Drawing.ToolboxBitmapAttribute))] +#endif + +#pragma warning restore CS0618 diff --git a/src/System.Drawing.Common/src/Interop/Windows/Comdlg32/Interop.PrintDlg.cs b/src/System.Drawing.Common/src/Interop/Windows/Comdlg32/Interop.PrintDlg.cs new file mode 100644 index 00000000000..e95066b1af3 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Comdlg32/Interop.PrintDlg.cs @@ -0,0 +1,79 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Comdlg32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Comdlg32, EntryPoint="PrintDlgW", SetLastError = true)] + internal static partial bool PrintDlg( +#else + [DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern bool PrintDlg( +#endif + ref PRINTDLG lppd); + + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Comdlg32, EntryPoint="PrintDlgW", SetLastError = true)] + internal static partial bool PrintDlg( +#else + [DllImport(Libraries.Comdlg32, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern bool PrintDlg( +#endif + ref PRINTDLGX86 lppd); + + [StructLayout(LayoutKind.Sequential)] + internal struct PRINTDLG + { + internal int lStructSize; + internal IntPtr hwndOwner; + internal IntPtr hDevMode; + internal IntPtr hDevNames; + internal IntPtr hDC; + internal int Flags; + internal short nFromPage; + internal short nToPage; + internal short nMinPage; + internal short nMaxPage; + internal short nCopies; + internal IntPtr hInstance; + internal IntPtr lCustData; + internal IntPtr lpfnPrintHook; + internal IntPtr lpfnSetupHook; + internal IntPtr lpPrintTemplateName; + internal IntPtr lpSetupTemplateName; + internal IntPtr hPrintTemplate; + internal IntPtr hSetupTemplate; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct PRINTDLGX86 + { + internal int lStructSize; + internal IntPtr hwndOwner; + internal IntPtr hDevMode; + internal IntPtr hDevNames; + internal IntPtr hDC; + internal int Flags; + internal short nFromPage; + internal short nToPage; + internal short nMinPage; + internal short nMaxPage; + internal short nCopies; + internal IntPtr hInstance; + internal IntPtr lCustData; + internal IntPtr lpfnPrintHook; + internal IntPtr lpfnSetupHook; + internal IntPtr lpPrintTemplateName; + internal IntPtr lpSetupTemplateName; + internal IntPtr hPrintTemplate; + internal IntPtr hSetupTemplate; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AbortDoc.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AbortDoc.cs new file mode 100644 index 00000000000..380e5f2b163 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AbortDoc.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int AbortDoc( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int AbortDoc( +#endif + HandleRef hDC); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AddFontResourceEx.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AddFontResourceEx.cs new file mode 100644 index 00000000000..265982115d8 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.AddFontResourceEx.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, EntryPoint = "AddFontResourceExW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int AddFontResourceEx( +#else + [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int AddFontResourceEx( +#endif + string lpszFilename, + int fl, + IntPtr pdv); + + internal static int AddFontFile(string fileName) + { + return AddFontResourceEx(fileName, /*FR_PRIVATE*/ 0x10, IntPtr.Zero); + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BITMAPINFO_FLAT.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BITMAPINFO_FLAT.cs new file mode 100644 index 00000000000..9cce923687d --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BITMAPINFO_FLAT.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + internal const int BITMAPINFO_MAX_COLORSIZE = 256; + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct BITMAPINFO_FLAT + { + public int bmiHeader_biSize; // = sizeof(BITMAPINFOHEADER) + public int bmiHeader_biWidth; + public int bmiHeader_biHeight; + public short bmiHeader_biPlanes; + public short bmiHeader_biBitCount; + public int bmiHeader_biCompression; + public int bmiHeader_biSizeImage; + public int bmiHeader_biXPelsPerMeter; + public int bmiHeader_biYPelsPerMeter; + public int bmiHeader_biClrUsed; + public int bmiHeader_biClrImportant; + + public fixed byte bmiColors[BITMAPINFO_MAX_COLORSIZE * 4]; // RGBQUAD structs... Blue-Green-Red-Reserved, repeat... + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs new file mode 100644 index 00000000000..bb9337753f9 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.BitBlt.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + public static partial int BitBlt( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true, SetLastError = true)] + public static extern int BitBlt( +#endif + IntPtr hdc, + int x, + int y, + int cx, + int cy, + IntPtr hdcSrc, + int x1, + int y1, + RasterOp rop); + + public static int BitBlt(HandleRef hdc, int x, int y, int cx, int cy, + HandleRef hdcSrc, int x1, int y1, RasterOp rop) + { + int result = BitBlt(hdc.Handle, x, y, cx, cy, hdcSrc.Handle, x1, y1, rop); + GC.KeepAlive(hdc.Wrapper); + GC.KeepAlive(hdcSrc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs new file mode 100644 index 00000000000..0ee693aef88 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CombineRgn.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + public enum CombineMode : int + { + RGN_AND = 1, + RGN_XOR = 3, + RGN_DIFF = 4, + } + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + public static partial RegionType CombineRgn( +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] + public static extern RegionType CombineRgn( +#endif + IntPtr hrgnDst, + IntPtr hrgnSrc1, + IntPtr hrgnSrc2, + CombineMode iMode); + + public static RegionType CombineRgn(HandleRef hrgnDst, HandleRef hrgnSrc1, HandleRef hrgnSrc2, CombineMode iMode) + { + RegionType result = CombineRgn(hrgnDst.Handle, hrgnSrc1.Handle, hrgnSrc2.Handle, iMode); + GC.KeepAlive(hrgnDst.Wrapper); + GC.KeepAlive(hrgnSrc1.Wrapper); + GC.KeepAlive(hrgnSrc2.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleBitmap.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleBitmap.cs new file mode 100644 index 00000000000..2a2c2fef0be --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleBitmap.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial IntPtr CreateCompatibleBitmap( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] + internal static extern IntPtr CreateCompatibleBitmap( +#endif + HandleRef hDC, + int width, + int height); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs new file mode 100644 index 00000000000..90e845b0b3b --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateCompatibleDC.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial IntPtr CreateCompatibleDC( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern IntPtr CreateCompatibleDC( +#endif + IntPtr hdc); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs new file mode 100644 index 00000000000..5d0e8dd2fef --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDC.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, StringMarshalling = StringMarshalling.Utf16)] + public static partial IntPtr CreateDCW( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateDCW( +#endif + string pwszDriver, + string pwszDevice, + string? pszPort, + IntPtr pdm); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDIBSection.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDIBSection.cs new file mode 100644 index 00000000000..6897bd06a44 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateDIBSection.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial IntPtr CreateDIBSection( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] + internal static extern IntPtr CreateDIBSection( +#endif + HandleRef hdc, + ref BITMAPINFO_FLAT bmi, + int iUsage, + ref IntPtr ppvBits, + IntPtr hSection, + int dwOffset); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs new file mode 100644 index 00000000000..a7ddc9c2b37 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateFontIndirect.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial IntPtr CreateFontIndirectW( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateFontIndirectW( +#endif + ref User32.LOGFONT lplf); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs new file mode 100644 index 00000000000..a581c5f4873 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateIC.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, StringMarshalling = StringMarshalling.Utf16)] + public static partial IntPtr CreateICW( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern IntPtr CreateICW( +#endif + string pszDriver, + string pszDevice, + string? pszPort, + IntPtr pdm); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs new file mode 100644 index 00000000000..7affc9428ca --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.CreateRectRgn.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial IntPtr CreateRectRgn( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern IntPtr CreateRectRgn( +#endif + int x1, + int y1, + int x2, + int y2); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DEVMODE.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DEVMODE.cs new file mode 100644 index 00000000000..aa26afa8566 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DEVMODE.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public sealed class DEVMODE + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string? dmDeviceName; + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + public short dmOrientation; + public short dmPaperSize; + public short dmPaperLength; + public short dmPaperWidth; + public short dmScale; + public short dmCopies; + public short dmDefaultSource; + public short dmPrintQuality; + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string? dmFormName; + public short dmLogPixels; + public int dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmICCManufacturer; + public int dmICCModel; + public int dmPanningWidth; + public int dmPanningHeight; + + + public override string ToString() + { + return "[DEVMODE: " + + "dmDeviceName=" + dmDeviceName + + ", dmSpecVersion=" + dmSpecVersion + + ", dmDriverVersion=" + dmDriverVersion + + ", dmSize=" + dmSize + + ", dmDriverExtra=" + dmDriverExtra + + ", dmFields=" + dmFields + + ", dmOrientation=" + dmOrientation + + ", dmPaperSize=" + dmPaperSize + + ", dmPaperLength=" + dmPaperLength + + ", dmPaperWidth=" + dmPaperWidth + + ", dmScale=" + dmScale + + ", dmCopies=" + dmCopies + + ", dmDefaultSource=" + dmDefaultSource + + ", dmPrintQuality=" + dmPrintQuality + + ", dmColor=" + dmColor + + ", dmDuplex=" + dmDuplex + + ", dmYResolution=" + dmYResolution + + ", dmTTOption=" + dmTTOption + + ", dmCollate=" + dmCollate + + ", dmFormName=" + dmFormName + + ", dmLogPixels=" + dmLogPixels + + ", dmBitsPerPel=" + dmBitsPerPel + + ", dmPelsWidth=" + dmPelsWidth + + ", dmPelsHeight=" + dmPelsHeight + + ", dmDisplayFlags=" + dmDisplayFlags + + ", dmDisplayFrequency=" + dmDisplayFrequency + + ", dmICMMethod=" + dmICMMethod + + ", dmICMIntent=" + dmICMIntent + + ", dmMediaType=" + dmMediaType + + ", dmDitherType=" + dmDitherType + + ", dmICCManufacturer=" + dmICCManufacturer + + ", dmICCModel=" + dmICCModel + + ", dmPanningWidth=" + dmPanningWidth + + ", dmPanningHeight=" + dmPanningHeight + + "]"; + } + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs new file mode 100644 index 00000000000..e5da740c46f --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteDC.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial bool DeleteDC( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern bool DeleteDC( +#endif + IntPtr hdc); + + public static bool DeleteDC(HandleRef hdc) + { + bool result = DeleteDC(hdc.Handle); + GC.KeepAlive(hdc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs new file mode 100644 index 00000000000..190dd3fe6f5 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.DeleteObject.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial bool DeleteObject( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern bool DeleteObject( +#endif + IntPtr ho); + + public static bool DeleteObject(HandleRef ho) + { + bool result = DeleteObject(ho.Handle); + GC.KeepAlive(ho.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndDoc.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndDoc.cs new file mode 100644 index 00000000000..163f5f0701e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndDoc.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int EndDoc( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int EndDoc( +#endif + HandleRef hDC); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndPage.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndPage.cs new file mode 100644 index 00000000000..f90b86a676a --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.EndPage.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int EndPage( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int EndPage( +#endif + HandleRef hDC); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ExtEscape.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ExtEscape.cs new file mode 100644 index 00000000000..705a387403a --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ExtEscape.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + internal const int QUERYESCSUPPORT = 8; + internal const int CHECKJPEGFORMAT = 4119; + internal const int CHECKPNGFORMAT = 4120; + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int ExtEscape( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int ExtEscape( +#endif + HandleRef hDC, + int nEscape, + int cbInput, + ref int inData, + int cbOutput, + out int outData); + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int ExtEscape( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int ExtEscape( +#endif + HandleRef hDC, + int nEscape, + int cbInput, + byte[] inData, + int cbOutput, + out int outData); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs new file mode 100644 index 00000000000..c108640d3fd --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetClipRgn.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial int GetClipRgn( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern int GetClipRgn( +#endif + IntPtr hdc, + IntPtr hrgn); + + public static int GetClipRgn(HandleRef hdc, IntPtr hrgn) + { + int result = GetClipRgn(hdc.Handle, hrgn); + GC.KeepAlive(hdc.Wrapper); + return result; + } + + public static int GetClipRgn(HandleRef hdc, HandleRef hrgn) + { + int result = GetClipRgn(hdc.Handle, hrgn.Handle); + GC.KeepAlive(hdc.Wrapper); + GC.KeepAlive(hrgn.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs new file mode 100644 index 00000000000..f970c78ca45 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetCurrentObject.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial IntPtr GetCurrentObject( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern IntPtr GetCurrentObject( +#endif + IntPtr hdc, + ObjectType type); + + public static IntPtr GetCurrentObject(HandleRef hdc, ObjectType type) + { + IntPtr result = GetCurrentObject(hdc.Handle, type); + GC.KeepAlive(hdc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDIBits.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDIBits.cs new file mode 100644 index 00000000000..45770913b09 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDIBits.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + internal static partial int GetDIBits( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32)] + internal static extern int GetDIBits( +#endif + HandleRef hdc, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hbm, + int arg1, + int arg2, + IntPtr arg3, + ref BITMAPINFO_FLAT bmi, + int arg5); + + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs new file mode 100644 index 00000000000..ce2e01f92b4 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetDeviceCaps.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + public enum DeviceCapability : int + { + TECHNOLOGY = 2, + VERTRES = 10, + HORZRES = 8, + BITSPIXEL = 12, + PLANES = 14, + LOGPIXELSX = 88, + LOGPIXELSY = 90, + PHYSICALWIDTH = 110, + PHYSICALHEIGHT = 111, + PHYSICALOFFSETX = 112, + PHYSICALOFFSETY = 113 + } + + public static class DeviceTechnology + { + public const int DT_PLOTTER = 0; + public const int DT_RASPRINTER = 2; + } + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial int GetDeviceCaps( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern int GetDeviceCaps( +#endif + IntPtr hdc, + DeviceCapability index); + + public static int GetDeviceCaps(HandleRef hdc, DeviceCapability index) + { + int caps = GetDeviceCaps(hdc.Handle, index); + GC.KeepAlive(hdc.Wrapper); + return caps; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObject.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObject.cs new file mode 100644 index 00000000000..a26531cc46e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObject.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, EntryPoint = "GetObjectW", SetLastError = true)] + internal static partial int GetObject( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true)] + internal static extern int GetObject( +#endif + HandleRef hObject, + int nSize, + ref BITMAP bm); + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, EntryPoint = "GetObjectW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GetObject( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern int GetObject( +#endif + HandleRef hObject, + int nSize, + ref Interop.User32.LOGFONT lf); + + internal static unsafe int GetObject( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hObject, + ref Interop.User32.LOGFONT lp) + => GetObject(hObject, sizeof(Interop.User32.LOGFONT), ref lp); + + [StructLayout(LayoutKind.Sequential)] + public struct BITMAP + { + public uint bmType; + public uint bmWidth; + public uint bmHeight; + public uint bmWidthBytes; + public ushort bmPlanes; + public ushort bmBitsPixel; + public IntPtr bmBits; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs new file mode 100644 index 00000000000..21b9f4be6c8 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetObjectType.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial ObjectType GetObjectType( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern ObjectType GetObjectType( +#endif + IntPtr h); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetPaletteEntries.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetPaletteEntries.cs new file mode 100644 index 00000000000..f21f0488911 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetPaletteEntries.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + internal static partial uint GetPaletteEntries( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32)] + internal static extern uint GetPaletteEntries( +#endif + HandleRef hpal, + int iStartIndex, + int nEntries, + byte[] lppe); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs new file mode 100644 index 00000000000..93209757bf1 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetRgnBox.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial RegionType GetRgnBox( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern RegionType GetRgnBox( +#endif + IntPtr hrgn, + ref RECT lprc); + + public static RegionType GetRgnBox(HandleRef hrgn, ref RECT lprc) + { + RegionType result = GetRgnBox(hrgn.Handle, ref lprc); + GC.KeepAlive(hrgn.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs new file mode 100644 index 00000000000..4dff4a7954b --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.GetStockObject.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + public enum StockObject : int + { + DEFAULT_GUI_FONT = 17 + } + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial IntPtr GetStockObject( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern IntPtr GetStockObject( +#endif + StockObject i); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.IntersectClipRect.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.IntersectClipRect.cs new file mode 100644 index 00000000000..fe9b35b2309 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.IntersectClipRect.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int IntersectClipRect( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int IntersectClipRect( +#endif + HandleRef hDC, + int x1, + int y1, + int x2, + int y2); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ObjectType.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ObjectType.cs new file mode 100644 index 00000000000..c0aa0cffcf6 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ObjectType.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + public enum ObjectType : int + { + OBJ_PEN = 1, + OBJ_BRUSH = 2, + OBJ_DC = 3, + OBJ_METADC = 4, + OBJ_PAL = 5, + OBJ_FONT = 6, + OBJ_BITMAP = 7, + OBJ_REGION = 8, + OBJ_METAFILE = 9, + OBJ_MEMDC = 10, + OBJ_EXTPEN = 11, + OBJ_ENHMETADC = 12, + OBJ_ENHMETAFILE = 13, + OBJ_COLORSPACE = 14 + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs new file mode 100644 index 00000000000..cf65a63638a --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.OffsetViewportOrgEx.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial bool OffsetViewportOrgEx( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern bool OffsetViewportOrgEx( +#endif + IntPtr hdc, + int x, + int y, + ref Point lppt); + + public static bool OffsetViewportOrgEx(HandleRef hdc, int x, int y, ref Point lppt) + { + bool result = OffsetViewportOrgEx(hdc.Handle, x, y, ref lppt); + GC.KeepAlive(hdc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RECT.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RECT.cs new file mode 100644 index 00000000000..fdd5de32a0e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RECT.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int left; + public int top; + public int right; + public int bottom; + + public Size Size => new Size(right - left, bottom - top); + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs new file mode 100644 index 00000000000..987a0cff614 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RasterOp.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + public enum RasterOp : int + { + SRCPAINT = 0x00EE0086, // dest = source OR dest + SRCAND = 0x008800C6, // dest = source AND dest + SRCCOPY = 0x00CC0020, + SRCINVERT = 0x00660046, // dest = source XOR dest + SRCERASE = 0x00440328, // dest = source AND (NOT dest ) + NOTSRCCOPY = 0x00330008, // dest = (NOT source) + NOTSRCERASE = 0x001100A6, // dest = (NOT src) AND (NOT dest) + MERGECOPY = 0x00C000CA, // dest = (source AND pattern) + MERGEPAINT = 0x00BB0226, // dest = (NOT source) OR dest + PATCOPY = 0x00F00021, // dest = pattern + PATPAINT = 0x00FB0A09, // dest = DPSnoo + PATINVERT = 0x005A0049, // dest = pattern XOR dest + DSTINVERT = 0x00550009, // dest = (NOT dest) + BLACKNESS = 0x00000042, // dest = BLACK + WHITENESS = 0x00FF0062, // dest = WHITE + CAPTUREBLT = 0x40000000, // Include layered windows + NOMIRRORBITMAP = unchecked((int)0x80000000), // Do not Mirror the bitmap in this call + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RegionType.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RegionType.cs new file mode 100644 index 00000000000..6039a732fcb --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RegionType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal enum RegionType : int + { + ERROR = 0, + NULLREGION = 1, + SIMPLEREGION = 2, + COMPLEXREGION = 3, + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ResetDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ResetDC.cs new file mode 100644 index 00000000000..04de763cabe --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.ResetDC.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, EntryPoint = "ResetDCW", SetLastError = true)] + internal static partial IntPtr /*HDC*/ ResetDC( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern IntPtr /*HDC*/ ResetDC( +#endif + HandleRef hDC, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef /*DEVMODE*/ lpDevMode); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs new file mode 100644 index 00000000000..73c90863c3e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.RestoreDC.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial bool RestoreDC( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern bool RestoreDC( +#endif + IntPtr hdc, + int nSavedDC); + + public static bool RestoreDC(HandleRef hdc, int nSavedDC) + { + bool result = RestoreDC(hdc.Handle, nSavedDC); + GC.KeepAlive(hdc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs new file mode 100644 index 00000000000..b1d5a312931 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SaveDC.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32)] + public static partial int SaveDC( +#else + [DllImport(Libraries.Gdi32, ExactSpelling = true)] + public static extern int SaveDC( +#endif + IntPtr hdc); + + public static int SaveDC(HandleRef hdc) + { + int result = SaveDC(hdc.Handle); + GC.KeepAlive(hdc.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs new file mode 100644 index 00000000000..192d76ef5d1 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.SelectClipRgn.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + public static partial RegionType SelectClipRgn( +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true)] + public static extern RegionType SelectClipRgn( +#endif + IntPtr hdc, + IntPtr hrgn); + + public static RegionType SelectClipRgn(HandleRef hdc, HandleRef hrgn) + { + RegionType result = SelectClipRgn(hdc.Handle, hrgn.Handle); + GC.KeepAlive(hdc.Wrapper); + GC.KeepAlive(hrgn.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartDoc.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartDoc.cs new file mode 100644 index 00000000000..e579a748645 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartDoc.cs @@ -0,0 +1,74 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, EntryPoint = "StartDocW", SetLastError = true)] + internal static partial int StartDoc( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int StartDoc( +#endif + HandleRef hDC, + in DOCINFO lpDocInfo); + +#if NET7_0_OR_GREATER + [NativeMarshalling(typeof(Marshaller))] +#endif + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct DOCINFO + { + internal int cbSize = 20; + internal string? lpszDocName; + internal string? lpszOutput; + internal string? lpszDatatype; + internal int fwType; + + public DOCINFO() { } + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(DOCINFO), MarshalMode.ManagedToUnmanagedIn, typeof(Marshaller))] + public static class Marshaller + { + public static Native ConvertToUnmanaged(DOCINFO managed) => new(managed); + public static void Free(Native native) => native.FreeNative(); + + internal struct Native + { + internal int cbSize; + internal IntPtr lpszDocName; + internal IntPtr lpszOutput; + internal IntPtr lpszDatatype; + internal int fwType; + + public Native(DOCINFO docInfo) + { + cbSize = docInfo.cbSize; + lpszDocName = Marshal.StringToCoTaskMemAuto(docInfo.lpszDocName); + lpszOutput = Marshal.StringToCoTaskMemAuto(docInfo.lpszOutput); + lpszDatatype = Marshal.StringToCoTaskMemAuto(docInfo.lpszDatatype); + fwType = docInfo.fwType; + } + + public void FreeNative() + { + Marshal.FreeCoTaskMem(lpszDocName); + Marshal.FreeCoTaskMem(lpszOutput); + Marshal.FreeCoTaskMem(lpszDatatype); + } + } + } +#endif + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartPage.cs b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartPage.cs new file mode 100644 index 00000000000..0d0e81b4b4d --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Gdi32/Interop.StartPage.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Gdi32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial int StartPage( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern int StartPage( +#endif + HandleRef hDC); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Interop.BOOL.cs b/src/System.Drawing.Common/src/Interop/Windows/Interop.BOOL.cs new file mode 100644 index 00000000000..619a726a75d --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Interop.BOOL.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + /// + /// Blittable version of Windows BOOL type. It is convenient in situations where + /// manual marshalling is required, or to avoid overhead of regular bool marshalling. + /// + /// + /// Some Windows APIs return arbitrary integer values although the return type is defined + /// as BOOL. It is best to never compare BOOL to TRUE. Always use bResult != BOOL.FALSE + /// or bResult == BOOL.FALSE . + /// + internal enum BOOL : int + { + FALSE = 0, + TRUE = 1, + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Interop.HRESULT.cs b/src/System.Drawing.Common/src/Interop/Windows/Interop.HRESULT.cs new file mode 100644 index 00000000000..b32676c03a7 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Interop.HRESULT.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + // https://msdn.microsoft.com/en-us/library/cc231198.aspx + internal enum HRESULT : int + { + S_OK = 0, + S_FALSE = 1, + E_NOTIMPL = unchecked((int)0x80004001), + E_ABORT = unchecked((int)0x80004004), + E_FAIL = unchecked((int)0x80004005), + E_UNEXPECTED = unchecked((int)0x8000FFFF), + STG_E_INVALIDFUNCTION = unchecked((int)0x80030001L), + STG_E_INVALIDPOINTER = unchecked((int)0x80030009), + STG_E_INVALIDPARAMETER = unchecked((int)0x80030057), + STG_E_INVALIDFLAG = unchecked((int)0x800300FF), + E_ACCESSDENIED = unchecked((int)0x80070005), + E_INVALIDARG = unchecked((int)0x80070057), + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Interop.Libraries.cs b/src/System.Drawing.Common/src/Interop/Windows/Interop.Libraries.cs new file mode 100644 index 00000000000..af66c1f796e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Interop.Libraries.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Libraries + { + internal const string Activeds = "activeds.dll"; + internal const string Advapi32 = "advapi32.dll"; + internal const string Authz = "authz.dll"; + internal const string BCrypt = "BCrypt.dll"; + internal const string Credui = "credui.dll"; + internal const string Crypt32 = "crypt32.dll"; + internal const string CryptUI = "cryptui.dll"; + internal const string Dnsapi = "dnsapi.dll"; + internal const string Dsrole = "dsrole.dll"; + internal const string Gdi32 = "gdi32.dll"; + internal const string HttpApi = "httpapi.dll"; + internal const string IpHlpApi = "iphlpapi.dll"; + internal const string Kernel32 = "kernel32.dll"; + internal const string Logoncli = "logoncli.dll"; + internal const string Mswsock = "mswsock.dll"; + internal const string NCrypt = "ncrypt.dll"; + internal const string Netapi32 = "netapi32.dll"; + internal const string Netutils = "netutils.dll"; + internal const string NtDll = "ntdll.dll"; + internal const string Odbc32 = "odbc32.dll"; + internal const string Ole32 = "ole32.dll"; + internal const string OleAut32 = "oleaut32.dll"; + internal const string Pdh = "pdh.dll"; + internal const string Secur32 = "secur32.dll"; + internal const string Shell32 = "shell32.dll"; + internal const string SspiCli = "sspicli.dll"; + internal const string User32 = "user32.dll"; + internal const string Version = "version.dll"; + internal const string WebSocket = "websocket.dll"; + internal const string Wevtapi = "wevtapi.dll"; + internal const string WinHttp = "winhttp.dll"; + internal const string WinMM = "winmm.dll"; + internal const string Wkscli = "wkscli.dll"; + internal const string Wldap32 = "wldap32.dll"; + internal const string Ws2_32 = "ws2_32.dll"; + internal const string Wtsapi32 = "wtsapi32.dll"; + internal const string CompressionNative = "System.IO.Compression.Native"; + internal const string GlobalizationNative = "System.Globalization.Native"; + internal const string MsQuic = "msquic.dll"; + internal const string HostPolicy = "hostpolicy"; + internal const string Ucrtbase = "ucrtbase.dll"; + internal const string Xolehlp = "xolehlp.dll"; + internal const string Comdlg32 = "comdlg32.dll"; + internal const string Gdiplus = "gdiplus.dll"; + internal const string Oleaut32 = "oleaut32.dll"; + internal const string Winspool = "winspool.drv"; + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GetSystemDefaultLCID.cs b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GetSystemDefaultLCID.cs new file mode 100644 index 00000000000..51d9fd22fb1 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GetSystemDefaultLCID.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Kernel32, SetLastError = true)] + public static partial int GetSystemDefaultLCID(); +#else + [DllImport(Libraries.Kernel32, SetLastError = true)] + public static extern int GetSystemDefaultLCID(); +#endif + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalAlloc.cs b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalAlloc.cs new file mode 100644 index 00000000000..b338556b884 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalAlloc.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Kernel32, EntryPoint = "GlobalAlloc", SetLastError = true)] + internal static partial IntPtr IntGlobalAlloc( +#else + [DllImport(Libraries.Kernel32, SetLastError = true, ExactSpelling = true, EntryPoint = "GlobalAlloc", CharSet = CharSet.Auto)] + internal static extern IntPtr IntGlobalAlloc( +#endif + int uFlags, + UIntPtr dwBytes); // size should be 32/64bits compatible + + internal static IntPtr GlobalAlloc(int uFlags, uint dwBytes) + { + return IntGlobalAlloc(uFlags, new UIntPtr(dwBytes)); + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs new file mode 100644 index 00000000000..3465a2447f9 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalFree.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Kernel32, SetLastError = true)] + public static partial IntPtr GlobalFree( +#else + [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GlobalFree( +#endif + IntPtr handle); + + public static IntPtr GlobalFree(HandleRef handle) + { + IntPtr result = GlobalFree(handle.Handle); + GC.KeepAlive(handle.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs new file mode 100644 index 00000000000..51165df6dd1 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.GlobalLock.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Kernel32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Kernel32, SetLastError = true)] + public static partial IntPtr GlobalLock( +#else + [DllImport(Libraries.Kernel32, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr GlobalLock( +#endif + IntPtr hMem); + + public static IntPtr GlobalLock(HandleRef hMem) + { + IntPtr result = GlobalLock(hMem.Handle); + GC.KeepAlive(hMem.Wrapper); + return result; + } + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Kernel32)] + public static partial IntPtr GlobalUnlock( +#else + [DllImport(Libraries.Kernel32, ExactSpelling = true)] + public static extern IntPtr GlobalUnlock( +#endif + IntPtr hMem); + + public static IntPtr GlobalUnlock(HandleRef hMem) + { + IntPtr result = GlobalUnlock(hMem.Handle); + GC.KeepAlive(hMem.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.SelectObject.cs b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.SelectObject.cs new file mode 100644 index 00000000000..d28d1ac7c5e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Kernel32/Interop.SelectObject.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Kernel32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Gdi32, SetLastError = true)] + internal static partial IntPtr SelectObject( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern IntPtr SelectObject( +#endif + HandleRef hdc, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef obj); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs new file mode 100644 index 00000000000..e922be50899 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.IStream.COMWrappers.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// IStream interface. + /// + /// + /// This interface explicitly doesn't use the built-in COM support, but instead is only used with ComWrappers. + /// + internal interface IStream + { + // pcbRead is optional + unsafe void Read(byte* pv, uint cb, uint* pcbRead); + + // pcbWritten is optional + unsafe void Write(byte* pv, uint cb, uint* pcbWritten); + + // SeekOrigin matches the native values, plibNewPosition is optional + unsafe void Seek(long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition); + + void SetSize(ulong libNewSize); + + // pcbRead and pcbWritten are optional + unsafe HRESULT CopyTo( + IntPtr pstm, + ulong cb, + ulong* pcbRead, + ulong* pcbWritten); + + void Commit(uint grfCommitFlags); + + void Revert(); + + HRESULT LockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + HRESULT UnlockRegion( + ulong libOffset, + ulong cb, + uint dwLockType); + + unsafe void Stat( + STATSTG* pstatstg, + STATFLAG grfStatFlag); + + unsafe HRESULT Clone(IntPtr* ppstm); + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs new file mode 100644 index 00000000000..38cd9844524 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATFLAG.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// Stat flags for . + /// + /// + internal enum STATFLAG : uint + { + /// + /// Stat includes the name. + /// + STATFLAG_DEFAULT = 0, + + /// + /// Stat doesn't include the name. + /// + STATFLAG_NONAME = 1 + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs new file mode 100644 index 00000000000..6e1cf2c7e86 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STATSTG.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// Statistics for . + /// + /// + /// + /// The definition in isn't blittable. + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct STATSTG + { + /// + /// Pointer to the name. + /// + private IntPtr pwcsName; + public STGTY type; + + /// + /// Size of the stream in bytes. + /// + public ulong cbSize; + + public FILETIME mtime; + public FILETIME ctime; + public FILETIME atime; + + /// + /// The stream mode. + /// + public STGM grfMode; + + /// + /// Supported locking modes. + /// + /// + /// + /// '0' means does not support lock modes. + /// + public uint grfLocksSupported; + + /// + /// Only for IStorage objects + /// + public Guid clsid; + + /// + /// Only valid for IStorage objects. + /// + public uint grfStateBits; + public uint reserved; + + public string? GetName() => Marshal.PtrToStringUni(pwcsName); + + /// + /// Caller is responsible for freeing the name memory. + /// + public void FreeName() + { + if (pwcsName != IntPtr.Zero) + Marshal.FreeCoTaskMem(pwcsName); + + pwcsName = IntPtr.Zero; + } + + /// + /// Callee is responsible for allocating the name memory. + /// + public void AllocName(string? name) + { + pwcsName = Marshal.StringToCoTaskMemUni(name); + } + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGM.cs b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGM.cs new file mode 100644 index 00000000000..a946ed209d4 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGM.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// Stream / storage modes. + /// + /// + [Flags] + internal enum STGM : uint + { + /// + /// Read only, and each change to a storage or stream element is written as it occurs. + /// Fails if the given storage object already exists. + /// [STGM_DIRECT] [STGM_READ] [STGM_FAILIFTHERE] [STGM_SHARE_DENY_WRITE] + /// + Default = 0x00000000, + + STGM_TRANSACTED = 0x00010000, + STGM_SIMPLE = 0x08000000, + STGM_WRITE = 0x00000001, + STGM_READWRITE = 0x00000002, + STGM_SHARE_DENY_NONE = 0x00000040, + STGM_SHARE_DENY_READ = 0x00000030, + STGM_SHARE_DENY_WRITE = 0x00000020, + STGM_SHARE_EXCLUSIVE = 0x00000010, + STGM_PRIORITY = 0x00040000, + STGM_DELETEONRELEASE = 0x04000000, + STGM_NOSCRATCH = 0x00100000, + STGM_CREATE = 0x00001000, + STGM_CONVERT = 0x00020000, + STGM_NOSNAPSHOT = 0x00200000, + STGM_DIRECT_SWMR = 0x00400000 + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGTY.cs b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGTY.cs new file mode 100644 index 00000000000..17be0418785 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Ole32/Interop.STGTY.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +internal static partial class Interop +{ + internal static partial class Ole32 + { + /// + /// Type of the storage element. Used with . + /// + /// + internal enum STGTY : uint + { + STGTY_STORAGE = 1, + STGTY_STREAM = 2, + STGTY_LOCKBYTES = 3, + STGTY_PROPERTY = 4 + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Shell32/Interop.ExtractAssociatedIcon.cs b/src/System.Drawing.Common/src/Interop/Windows/Shell32/Interop.ExtractAssociatedIcon.cs new file mode 100644 index 00000000000..d5bf71ea98e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Shell32/Interop.ExtractAssociatedIcon.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Shell32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Shell32, EntryPoint = "ExtractAssociatedIconW")] + internal static unsafe partial IntPtr ExtractAssociatedIcon( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Shell32, CharSet = CharSet.Unicode)] + internal static extern unsafe IntPtr ExtractAssociatedIcon( +#endif + HandleRef hInst, + char* iconPath, + ref int index); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CopyImage.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CopyImage.cs new file mode 100644 index 00000000000..66285a92aae --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CopyImage.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial IntPtr CopyImage( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] + internal static extern IntPtr CopyImage( +#endif + HandleRef hImage, + int uType, + int cxDesired, + int cyDesired, + int fuFlags); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CreateIconFromResourceEx.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CreateIconFromResourceEx.cs new file mode 100644 index 00000000000..aaf7fa0b7ed --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.CreateIconFromResourceEx.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static unsafe partial IntPtr CreateIconFromResourceEx( +#else + [DllImport(Libraries.User32, ExactSpelling = true, SetLastError = true)] + internal static extern unsafe IntPtr CreateIconFromResourceEx( +#endif + byte* pbIconBits, + uint cbIconBits, + [MarshalAs(UnmanagedType.Bool)] bool fIcon, + int dwVersion, + int csDesired, + int cyDesired, + int flags); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DestroyIcon.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DestroyIcon.cs new file mode 100644 index 00000000000..dfde53c6591 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DestroyIcon.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial bool DestroyIcon( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] + internal static extern bool DestroyIcon( +#endif + HandleRef hIcon); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DrawIconEx.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DrawIconEx.cs new file mode 100644 index 00000000000..453d9945fba --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.DrawIconEx.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial bool DrawIconEx( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + internal static extern bool DrawIconEx( +#endif + HandleRef hDC, + int x, + int y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hIcon, + int width, + int height, + int iStepIfAniCursor, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hBrushFlickerFree, + int diFlags); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetDC.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetDC.cs new file mode 100644 index 00000000000..be3e7ab1293 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetDC.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32)] + public static partial IntPtr GetDC( +#else + [DllImport(Libraries.User32, ExactSpelling = true)] + public static extern IntPtr GetDC( +#endif + IntPtr hWnd); + + public static IntPtr GetDC(HandleRef hWnd) + { + IntPtr dc = GetDC(hWnd.Handle); + GC.KeepAlive(hWnd.Wrapper); + return dc; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetIconInfo.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetIconInfo.cs new file mode 100644 index 00000000000..149f58d5afb --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetIconInfo.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + internal static partial bool GetIconInfo( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] + internal static extern bool GetIconInfo( +#endif + HandleRef hIcon, + ref ICONINFO info); + + [StructLayout(LayoutKind.Sequential)] + internal struct ICONINFO + { + internal uint fIcon; + internal uint xHotspot; + internal uint yHotspot; + internal IntPtr hbmMask; + internal IntPtr hbmColor; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetSystemMetrics.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetSystemMetrics.cs new file mode 100644 index 00000000000..a039573dbc5 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.GetSystemMetrics.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, SetLastError = true)] + public static partial int GetSystemMetrics( +#else + [DllImport(Libraries.User32, SetLastError = true, ExactSpelling = true)] + public static extern int GetSystemMetrics( +#endif + int nIndex); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LOGFONT.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LOGFONT.cs new file mode 100644 index 00000000000..47193fe3f8e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LOGFONT.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public unsafe struct LOGFONT + { + private const int LF_FACESIZE = 32; + + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + private fixed char _lfFaceName[LF_FACESIZE]; + public Span lfFaceName => MemoryMarshal.CreateSpan(ref _lfFaceName[0], LF_FACESIZE); + + public override string ToString() + { + return + "lfHeight=" + lfHeight + ", " + + "lfWidth=" + lfWidth + ", " + + "lfEscapement=" + lfEscapement + ", " + + "lfOrientation=" + lfOrientation + ", " + + "lfWeight=" + lfWeight + ", " + + "lfItalic=" + lfItalic + ", " + + "lfUnderline=" + lfUnderline + ", " + + "lfStrikeOut=" + lfStrikeOut + ", " + + "lfCharSet=" + lfCharSet + ", " + + "lfOutPrecision=" + lfOutPrecision + ", " + + "lfClipPrecision=" + lfClipPrecision + ", " + + "lfQuality=" + lfQuality + ", " + + "lfPitchAndFamily=" + lfPitchAndFamily + ", " + + "lfFaceName=" + lfFaceName.ToString(); + } + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LoadIcon.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LoadIcon.cs new file mode 100644 index 00000000000..0644a5b48c3 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.LoadIcon.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32, EntryPoint = "LoadIconW", SetLastError = true)] + internal static partial IntPtr LoadIcon( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.User32, SetLastError = true, CharSet = CharSet.Unicode)] + internal static extern IntPtr LoadIcon( +#endif + HandleRef hInst, IntPtr iconId); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.NONCLIENTMETRICS.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.NONCLIENTMETRICS.cs new file mode 100644 index 00000000000..4bd7bbfd809 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.NONCLIENTMETRICS.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct NONCLIENTMETRICS + { + public uint cbSize; + public int iBorderWidth; + public int iScrollWidth; + public int iScrollHeight; + public int iCaptionWidth; + public int iCaptionHeight; + public LOGFONT lfCaptionFont; + public int iSmCaptionWidth; + public int iSmCaptionHeight; + public LOGFONT lfSmCaptionFont; + public int iMenuWidth; + public int iMenuHeight; + public LOGFONT lfMenuFont; + public LOGFONT lfStatusFont; + public LOGFONT lfMessageFont; + public int iPaddedBorderWidth; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs new file mode 100644 index 00000000000..fb0987f8b10 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.ReleaseDC.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32)] + public static partial int ReleaseDC( +#else + [DllImport(Libraries.User32, ExactSpelling = true)] + public static extern int ReleaseDC( +#endif + IntPtr hWnd, + IntPtr hDC); + + public static int ReleaseDC(HandleRef hWnd, IntPtr hDC) + { + int result = ReleaseDC(hWnd.Handle, hDC); + GC.KeepAlive(hWnd.Wrapper); + return result; + } + + public static int ReleaseDC(HandleRef hWnd, HandleRef hDC) + { + int result = ReleaseDC(hWnd.Handle, hDC.Handle); + GC.KeepAlive(hWnd.Wrapper); + GC.KeepAlive(hDC.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs new file mode 100644 index 00000000000..b29412efd19 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.SystemParametersInfo.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { + public enum SystemParametersAction : uint + { + SPI_GETICONTITLELOGFONT = 0x1F, + SPI_GETNONCLIENTMETRICS = 0x29 + } + + [return: MarshalAs(UnmanagedType.Bool)] +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32)] + public static unsafe partial bool SystemParametersInfoW( +#else + [DllImport(Libraries.User32, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern unsafe bool SystemParametersInfoW( +#endif + SystemParametersAction uiAction, + uint uiParam, + void* pvParam, + uint fWinIni); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs new file mode 100644 index 00000000000..8b4565d572d --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/User32/Interop.WindowFromDC.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class User32 + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.User32)] + public static partial IntPtr WindowFromDC( +#else + [DllImport(Libraries.User32, ExactSpelling = true)] + public static extern IntPtr WindowFromDC( +#endif + IntPtr hDC); + + public static IntPtr WindowFromDC(HandleRef hDC) + { + IntPtr result = WindowFromDC(hDC.Handle); + GC.KeepAlive(hDC.Wrapper); + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DeviceCapabilities.cs b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DeviceCapabilities.cs new file mode 100644 index 00000000000..60b9fe58ed6 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DeviceCapabilities.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; + +internal static partial class Interop +{ + internal static partial class Winspool + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Winspool, EntryPoint = "DeviceCapabilitiesW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int DeviceCapabilities( +#else + [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int DeviceCapabilities( +#endif + string pDevice, + string pPort, + short fwCapabilities, + IntPtr pOutput, + IntPtr /*DEVMODE*/ pDevMode); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DocumentProperties.cs b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DocumentProperties.cs new file mode 100644 index 00000000000..36eb63e9697 --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.DocumentProperties.cs @@ -0,0 +1,53 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Winspool + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Winspool, EntryPoint = "DocumentPropertiesW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int DocumentProperties( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] + internal static extern int DocumentProperties( +#endif + HandleRef hwnd, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hPrinter, + string pDeviceName, + IntPtr /*DEVMODE*/ pDevModeOutput, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef /*DEVMODE*/ pDevModeInput, + int fMode); + +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Winspool, EntryPoint = "DocumentPropertiesW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int DocumentProperties( + [MarshalUsing(typeof(HandleRefMarshaller))] +#else + [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] + internal static extern int DocumentProperties( +#endif + HandleRef hwnd, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef hPrinter, + string pDeviceName, + IntPtr /*DEVMODE*/ pDevModeOutput, + IntPtr /*DEVMODE*/ pDevModeInput, + int fMode); + } +} diff --git a/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.EnumPrinters.cs b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.EnumPrinters.cs new file mode 100644 index 00000000000..e43bdc9875e --- /dev/null +++ b/src/System.Drawing.Common/src/Interop/Windows/Winspool/Interop.EnumPrinters.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +internal static partial class Interop +{ + internal static partial class Winspool + { +#if NET7_0_OR_GREATER + [LibraryImport(Libraries.Winspool, EntryPoint = "EnumPrintersW", SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int EnumPrinters( +#else + [DllImport(Libraries.Winspool, SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int EnumPrinters( +#endif + int flags, + string? name, + int level, + IntPtr pPrinterEnum/*buffer*/, + int cbBuf, + out int pcbNeeded, + out int pcReturned); + } +} diff --git a/src/System.Drawing.Common/src/NotSupported.cs b/src/System.Drawing.Common/src/NotSupported.cs new file mode 100644 index 00000000000..e07b38e887c --- /dev/null +++ b/src/System.Drawing.Common/src/NotSupported.cs @@ -0,0 +1,3078 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// ------------------------------------------------------------------------------ +// Changes to this file must follow the https://aka.ms/api-review process. +// ------------------------------------------------------------------------------ + +#pragma warning disable CS8618,CS0169,CA1823,CA1066 + +namespace System.Drawing +{ + [System.ComponentModel.EditorAttribute("System.Drawing.Design.BitmapEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed partial class Bitmap : System.Drawing.Image + { + public Bitmap(System.Drawing.Image original) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(System.Drawing.Image original, System.Drawing.Size newSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(System.Drawing.Image original, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(int width, int height, System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(int width, int height, System.Drawing.Imaging.PixelFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(int width, int height, int stride, System.Drawing.Imaging.PixelFormat format, System.IntPtr scan0) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(System.IO.Stream stream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(System.IO.Stream stream, bool useIcm) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(string filename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(string filename, bool useIcm) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Bitmap(System.Type type, string resource) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Bitmap Clone(System.Drawing.Rectangle rect, System.Drawing.Imaging.PixelFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Bitmap Clone(System.Drawing.RectangleF rect, System.Drawing.Imaging.PixelFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Bitmap FromHicon(System.IntPtr hicon) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Bitmap FromResource(System.IntPtr hinstance, string bitmapName) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public System.IntPtr GetHbitmap() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public System.IntPtr GetHbitmap(System.Drawing.Color background) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public System.IntPtr GetHicon() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color GetPixel(int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.BitmapData LockBits(System.Drawing.Rectangle rect, System.Drawing.Imaging.ImageLockMode flags, System.Drawing.Imaging.PixelFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.BitmapData LockBits(System.Drawing.Rectangle rect, System.Drawing.Imaging.ImageLockMode flags, System.Drawing.Imaging.PixelFormat format, System.Drawing.Imaging.BitmapData bitmapData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MakeTransparent() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MakeTransparent(System.Drawing.Color transparentColor) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetPixel(int x, int y, System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetResolution(float xDpi, float yDpi) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void UnlockBits(System.Drawing.Imaging.BitmapData bitmapdata) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly)] + public partial class BitmapSuffixInSameAssemblyAttribute : System.Attribute + { + public BitmapSuffixInSameAssemblyAttribute() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Assembly)] + public partial class BitmapSuffixInSatelliteAssemblyAttribute : System.Attribute + { + public BitmapSuffixInSatelliteAssemblyAttribute() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public abstract partial class Brush : System.MarshalByRefObject, System.ICloneable, System.IDisposable + { + protected Brush() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public abstract object Clone(); + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + ~Brush() { } + protected internal void SetNativeBrush(System.IntPtr brush) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class Brushes + { + public static System.Drawing.Brush AliceBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush AntiqueWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Aqua { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Aquamarine { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Azure { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Beige { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Bisque { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Black { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush BlanchedAlmond { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Blue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush BlueViolet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Brown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush BurlyWood { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush CadetBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Chartreuse { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Chocolate { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Coral { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush CornflowerBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Cornsilk { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Crimson { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Cyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkCyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkGoldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkKhaki { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkMagenta { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkOliveGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkOrange { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkOrchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkSalmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkSlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkSlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DarkViolet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DeepPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DeepSkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DimGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush DodgerBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Firebrick { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush FloralWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ForestGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Fuchsia { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Gainsboro { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush GhostWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Gold { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Goldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Gray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Green { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush GreenYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Honeydew { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush HotPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush IndianRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Indigo { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Ivory { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Khaki { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Lavender { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LavenderBlush { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LawnGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LemonChiffon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightCoral { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightCyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightGoldenrodYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightSalmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightSkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightSlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightSteelBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LightYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Lime { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush LimeGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Linen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Magenta { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Maroon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumAquamarine { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumOrchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumPurple { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumSlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumSpringGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MediumVioletRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MidnightBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MintCream { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MistyRose { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Moccasin { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush NavajoWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Navy { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush OldLace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Olive { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush OliveDrab { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Orange { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush OrangeRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Orchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PaleGoldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PaleGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PaleTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PaleVioletRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PapayaWhip { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PeachPuff { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Peru { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Pink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Plum { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush PowderBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Purple { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Red { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush RosyBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush RoyalBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SaddleBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Salmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SandyBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SeaShell { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Sienna { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Silver { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Snow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SpringGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush SteelBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Tan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Teal { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Thistle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Tomato { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Transparent { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Turquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Violet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Wheat { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush White { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush WhiteSmoke { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Yellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush YellowGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class BufferedGraphics : System.IDisposable + { + internal BufferedGraphics() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Graphics Graphics { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void Dispose() { } + public void Render() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Render(System.Drawing.Graphics? target) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Render(System.IntPtr targetDC) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class BufferedGraphicsContext : System.IDisposable + { + public BufferedGraphicsContext() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Size MaximumBuffer { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.BufferedGraphics Allocate(System.Drawing.Graphics targetGraphics, System.Drawing.Rectangle targetRectangle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.BufferedGraphics Allocate(System.IntPtr targetDC, System.Drawing.Rectangle targetRectangle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + ~BufferedGraphicsContext() { } + public void Invalidate() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class BufferedGraphicsManager + { + public static System.Drawing.BufferedGraphicsContext Current { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public partial struct CharacterRange + { + private int _dummyPrimitive; + public CharacterRange(int First, int Length) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int First { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Length { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool operator ==(System.Drawing.CharacterRange cr1, System.Drawing.CharacterRange cr2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool operator !=(System.Drawing.CharacterRange cr1, System.Drawing.CharacterRange cr2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class ColorTranslator + { + public static System.Drawing.Color FromHtml(string htmlColor) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Color FromOle(int oleColor) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Color FromWin32(int win32Color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static string ToHtml(System.Drawing.Color c) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static int ToOle(System.Drawing.Color c) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static int ToWin32(System.Drawing.Color c) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.ComponentModel.EditorAttribute("System.Drawing.Design.ContentAlignmentEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public enum ContentAlignment + { + TopLeft = 1, + TopCenter = 2, + TopRight = 4, + MiddleLeft = 16, + MiddleCenter = 32, + MiddleRight = 64, + BottomLeft = 256, + BottomCenter = 512, + BottomRight = 1024, + } + public enum CopyPixelOperation + { + NoMirrorBitmap = -2147483648, + Blackness = 66, + NotSourceErase = 1114278, + NotSourceCopy = 3342344, + SourceErase = 4457256, + DestinationInvert = 5570569, + PatInvert = 5898313, + SourceInvert = 6684742, + SourceAnd = 8913094, + MergePaint = 12255782, + MergeCopy = 12583114, + SourceCopy = 13369376, + SourcePaint = 15597702, + PatCopy = 15728673, + PatPaint = 16452105, + Whiteness = 16711778, + CaptureBlt = 1073741824, + } + [System.ComponentModel.EditorAttribute("System.Drawing.Design.FontEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.FontConverter))] + public sealed partial class Font : System.MarshalByRefObject, System.ICloneable, System.IDisposable, System.Runtime.Serialization.ISerializable + { + public Font(System.Drawing.Font prototype, System.Drawing.FontStyle newStyle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize, System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit, byte gdiCharSet) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(System.Drawing.FontFamily family, float emSize, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize, System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit, byte gdiCharSet) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize, System.Drawing.FontStyle style, System.Drawing.GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Font(string familyName, float emSize, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public bool Bold { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.FontFamily FontFamily { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public byte GdiCharSet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public bool GdiVerticalFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsSystemFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public bool Italic { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + [System.ComponentModel.EditorAttribute("System.Drawing.Design.FontNameEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.FontConverter.FontNameConverter))] + public string Name { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public string? OriginalFontName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Size { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public float SizeInPoints { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public bool Strikeout { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.FontStyle Style { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public string SystemFontName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public bool Underline { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.FontConverter.FontUnitConverter))] + public System.Drawing.GraphicsUnit Unit { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~Font() { } + public static System.Drawing.Font FromHdc(System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Font FromHfont(System.IntPtr hfont) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Font FromLogFont(object lf) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Font FromLogFont(object lf, System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float GetHeight() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float GetHeight(System.Drawing.Graphics graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float GetHeight(float dpi) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr ToHfont() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ToLogFont(object logFont) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ToLogFont(object logFont, System.Drawing.Graphics graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class FontConverter : System.ComponentModel.TypeConverter + { + public FontConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + public override System.ComponentModel.PropertyDescriptorCollection? GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public sealed partial class FontNameConverter : System.ComponentModel.TypeConverter, System.IDisposable + { + public FontNameConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetStandardValuesExclusive(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.IDisposable.Dispose() { } + } + public partial class FontUnitConverter : System.ComponentModel.EnumConverter + { + public FontUnitConverter() : base (default(System.Type)) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + } + public sealed partial class FontFamily : System.MarshalByRefObject, System.IDisposable + { + public FontFamily(System.Drawing.Text.GenericFontFamilies genericFamily) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public FontFamily(string name) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public FontFamily(string name, System.Drawing.Text.FontCollection? fontCollection) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.FontFamily[] Families { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.FontFamily GenericMonospace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.FontFamily GenericSansSerif { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.FontFamily GenericSerif { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string Name { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void Dispose() { } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~FontFamily() { } + public int GetCellAscent(System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int GetCellDescent(System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int GetEmHeight(System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ObsoleteAttribute("FontFamily.GetFamilies has been deprecated. Use Families instead.")] + public static System.Drawing.FontFamily[] GetFamilies(System.Drawing.Graphics graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int GetLineSpacing(System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public string GetName(int language) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsStyleAvailable(System.Drawing.FontStyle style) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.FlagsAttribute] + public enum FontStyle + { + Regular = 0, + Bold = 1, + Italic = 2, + Underline = 4, + Strikeout = 8, + } + public sealed partial class Graphics : System.MarshalByRefObject, System.Drawing.IDeviceContext, System.IDisposable + { + internal Graphics() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Region Clip { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.RectangleF ClipBounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.CompositingMode CompositingMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.CompositingQuality CompositingQuality { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float DpiX { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float DpiY { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.InterpolationMode InterpolationMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsClipEmpty { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsVisibleClipEmpty { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float PageScale { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.GraphicsUnit PageUnit { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.PixelOffsetMode PixelOffsetMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Point RenderingOrigin { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.SmoothingMode SmoothingMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int TextContrast { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Text.TextRenderingHint TextRenderingHint { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Transform { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.RectangleF VisibleClipBounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void AddMetafileComment(byte[] data) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.GraphicsContainer BeginContainer() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.GraphicsContainer BeginContainer(System.Drawing.Rectangle dstrect, System.Drawing.Rectangle srcrect, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.GraphicsContainer BeginContainer(System.Drawing.RectangleF dstrect, System.Drawing.RectangleF srcrect, System.Drawing.GraphicsUnit unit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Clear(System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyFromScreen(System.Drawing.Point upperLeftSource, System.Drawing.Point upperLeftDestination, System.Drawing.Size blockRegionSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyFromScreen(System.Drawing.Point upperLeftSource, System.Drawing.Point upperLeftDestination, System.Drawing.Size blockRegionSize, System.Drawing.CopyPixelOperation copyPixelOperation) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, System.Drawing.Size blockRegionSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, System.Drawing.Size blockRegionSize, System.Drawing.CopyPixelOperation copyPixelOperation) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public void DrawArc(System.Drawing.Pen pen, System.Drawing.Rectangle rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawArc(System.Drawing.Pen pen, System.Drawing.RectangleF rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawArc(System.Drawing.Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawArc(System.Drawing.Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawBezier(System.Drawing.Pen pen, System.Drawing.Point pt1, System.Drawing.Point pt2, System.Drawing.Point pt3, System.Drawing.Point pt4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawBezier(System.Drawing.Pen pen, System.Drawing.PointF pt1, System.Drawing.PointF pt2, System.Drawing.PointF pt3, System.Drawing.PointF pt4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawBezier(System.Drawing.Pen pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawBeziers(System.Drawing.Pen pen, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawBeziers(System.Drawing.Pen pen, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawClosedCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawClosedCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points, float tension, System.Drawing.Drawing2D.FillMode fillmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawClosedCurve(System.Drawing.Pen pen, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawClosedCurve(System.Drawing.Pen pen, System.Drawing.Point[] points, float tension, System.Drawing.Drawing2D.FillMode fillmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points, int offset, int numberOfSegments) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points, int offset, int numberOfSegments, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.PointF[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.Point[] points, int offset, int numberOfSegments, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawCurve(System.Drawing.Pen pen, System.Drawing.Point[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawEllipse(System.Drawing.Pen pen, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawEllipse(System.Drawing.Pen pen, System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawEllipse(System.Drawing.Pen pen, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawEllipse(System.Drawing.Pen pen, float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawIcon(System.Drawing.Icon icon, System.Drawing.Rectangle targetRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawIcon(System.Drawing.Icon icon, int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawIconUnstretched(System.Drawing.Icon icon, System.Drawing.Rectangle targetRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF[] destPoints) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr, System.Drawing.Graphics.DrawImageAbort? callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr, System.Drawing.Graphics.DrawImageAbort? callback, int callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point[] destPoints) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr, System.Drawing.Graphics.DrawImageAbort? callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr, System.Drawing.Graphics.DrawImageAbort? callback, int callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttr, System.Drawing.Graphics.DrawImageAbort? callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, int srcX, int srcY, int srcWidth, int srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttrs, System.Drawing.Graphics.DrawImageAbort? callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttrs) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttrs, System.Drawing.Graphics.DrawImageAbort? callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Imaging.ImageAttributes? imageAttrs, System.Drawing.Graphics.DrawImageAbort? callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, System.Drawing.RectangleF destRect, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, int x, int y, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, float x, float y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, float x, float y, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImage(System.Drawing.Image image, float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImageUnscaled(System.Drawing.Image image, System.Drawing.Point point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImageUnscaled(System.Drawing.Image image, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImageUnscaled(System.Drawing.Image image, int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImageUnscaled(System.Drawing.Image image, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawImageUnscaledAndClipped(System.Drawing.Image image, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLine(System.Drawing.Pen pen, System.Drawing.Point pt1, System.Drawing.Point pt2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLine(System.Drawing.Pen pen, System.Drawing.PointF pt1, System.Drawing.PointF pt2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLine(System.Drawing.Pen pen, int x1, int y1, int x2, int y2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLine(System.Drawing.Pen pen, float x1, float y1, float x2, float y2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLines(System.Drawing.Pen pen, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawLines(System.Drawing.Pen pen, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPath(System.Drawing.Pen pen, System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPie(System.Drawing.Pen pen, System.Drawing.Rectangle rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPie(System.Drawing.Pen pen, System.Drawing.RectangleF rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPie(System.Drawing.Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPie(System.Drawing.Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPolygon(System.Drawing.Pen pen, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawPolygon(System.Drawing.Pen pen, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawRectangle(System.Drawing.Pen pen, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawRectangle(System.Drawing.Pen pen, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawRectangle(System.Drawing.Pen pen, float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawRectangles(System.Drawing.Pen pen, System.Drawing.RectangleF[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawRectangles(System.Drawing.Pen pen, System.Drawing.Rectangle[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.PointF point, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, System.Drawing.RectangleF layoutRectangle, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void DrawString(string? s, System.Drawing.Font font, System.Drawing.Brush brush, float x, float y, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EndContainer(System.Drawing.Drawing2D.GraphicsContainer container) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point destPoint, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF destPoint, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Point[] destPoints, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.Rectangle destRect, System.Drawing.Rectangle srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit srcUnit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void EnumerateMetafile(System.Drawing.Imaging.Metafile metafile, System.Drawing.RectangleF destRect, System.Drawing.RectangleF srcRect, System.Drawing.GraphicsUnit unit, System.Drawing.Graphics.EnumerateMetafileProc callback, System.IntPtr callbackData, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ExcludeClip(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ExcludeClip(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.PointF[] points, System.Drawing.Drawing2D.FillMode fillmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.PointF[] points, System.Drawing.Drawing2D.FillMode fillmode, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.Point[] points, System.Drawing.Drawing2D.FillMode fillmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillClosedCurve(System.Drawing.Brush brush, System.Drawing.Point[] points, System.Drawing.Drawing2D.FillMode fillmode, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillEllipse(System.Drawing.Brush brush, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillEllipse(System.Drawing.Brush brush, System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillEllipse(System.Drawing.Brush brush, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillEllipse(System.Drawing.Brush brush, float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPath(System.Drawing.Brush brush, System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPie(System.Drawing.Brush brush, System.Drawing.Rectangle rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPie(System.Drawing.Brush brush, int x, int y, int width, int height, int startAngle, int sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPie(System.Drawing.Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPolygon(System.Drawing.Brush brush, System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPolygon(System.Drawing.Brush brush, System.Drawing.PointF[] points, System.Drawing.Drawing2D.FillMode fillMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPolygon(System.Drawing.Brush brush, System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillPolygon(System.Drawing.Brush brush, System.Drawing.Point[] points, System.Drawing.Drawing2D.FillMode fillMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangle(System.Drawing.Brush brush, System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangle(System.Drawing.Brush brush, System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangle(System.Drawing.Brush brush, int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangle(System.Drawing.Brush brush, float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangles(System.Drawing.Brush brush, System.Drawing.RectangleF[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRectangles(System.Drawing.Brush brush, System.Drawing.Rectangle[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void FillRegion(System.Drawing.Brush brush, System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~Graphics() { } + public void Flush() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Flush(System.Drawing.Drawing2D.FlushIntention intention) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Drawing.Graphics FromHdc(System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Drawing.Graphics FromHdc(System.IntPtr hdc, System.IntPtr hdevice) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Drawing.Graphics FromHdcInternal(System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Drawing.Graphics FromHwnd(System.IntPtr hwnd) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Drawing.Graphics FromHwndInternal(System.IntPtr hwnd) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Graphics FromImage(System.Drawing.Image image) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] + public object GetContextInfo() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.IntPtr GetHalftonePalette() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHdc() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color GetNearestColor(System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void IntersectClip(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void IntersectClip(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void IntersectClip(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Point point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Region[] MeasureCharacterRanges(string? text, System.Drawing.Font font, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? stringFormat) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.PointF origin, System.Drawing.StringFormat? stringFormat) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, System.Drawing.SizeF layoutArea, System.Drawing.StringFormat? stringFormat, out int charactersFitted, out int linesFilled) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, int width) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.SizeF MeasureString(string? text, System.Drawing.Font font, int width, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ReleaseHdc() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public void ReleaseHdc(System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public void ReleaseHdcInternal(System.IntPtr hdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetClip() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetTransform() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Restore(System.Drawing.Drawing2D.GraphicsState gstate) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.GraphicsState Save() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Drawing2D.GraphicsPath path, System.Drawing.Drawing2D.CombineMode combineMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Graphics g, System.Drawing.Drawing2D.CombineMode combineMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Rectangle rect, System.Drawing.Drawing2D.CombineMode combineMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.RectangleF rect, System.Drawing.Drawing2D.CombineMode combineMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetClip(System.Drawing.Region region, System.Drawing.Drawing2D.CombineMode combineMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformPoints(System.Drawing.Drawing2D.CoordinateSpace destSpace, System.Drawing.Drawing2D.CoordinateSpace srcSpace, System.Drawing.PointF[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformPoints(System.Drawing.Drawing2D.CoordinateSpace destSpace, System.Drawing.Drawing2D.CoordinateSpace srcSpace, System.Drawing.Point[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateClip(int dx, int dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateClip(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public delegate bool DrawImageAbort(System.IntPtr callbackdata); + public delegate bool EnumerateMetafileProc(System.Drawing.Imaging.EmfPlusRecordType recordType, int flags, int dataSize, System.IntPtr data, System.Drawing.Imaging.PlayRecordCallback? callbackData); + } + public enum GraphicsUnit + { + World = 0, + Display = 1, + Pixel = 2, + Point = 3, + Inch = 4, + Document = 5, + Millimeter = 6, + } + [System.ComponentModel.EditorAttribute("System.Drawing.Design.IconEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.IconConverter))] + public sealed partial class Icon : System.MarshalByRefObject, System.ICloneable, System.IDisposable, System.Runtime.Serialization.ISerializable + { + public Icon(System.Drawing.Icon original, System.Drawing.Size size) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(System.Drawing.Icon original, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(System.IO.Stream stream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(System.IO.Stream stream, System.Drawing.Size size) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(System.IO.Stream stream, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(string fileName) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(string fileName, System.Drawing.Size size) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(string fileName, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Icon(System.Type type, string resource) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.BrowsableAttribute(false)] + public System.IntPtr Handle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Size Size { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public int Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public static System.Drawing.Icon? ExtractAssociatedIcon(string filePath) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~Icon() { } + public static System.Drawing.Icon FromHandle(System.IntPtr handle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(System.IO.Stream outputStream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Bitmap ToBitmap() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class IconConverter : System.ComponentModel.ExpandableObjectConverter + { + public IconConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial interface IDeviceContext : System.IDisposable + { + System.IntPtr GetHdc(); + void ReleaseHdc(); + } + [System.ComponentModel.EditorAttribute("System.Drawing.Design.ImageEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [System.ComponentModel.ImmutableObjectAttribute(true)] + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.ImageConverter))] + public abstract partial class Image : System.MarshalByRefObject, System.ICloneable, System.IDisposable, System.Runtime.Serialization.ISerializable + { + internal Image() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.BrowsableAttribute(false)] + public int Flags { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Guid[] FrameDimensionsList { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + [System.ComponentModel.DefaultValueAttribute(false)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public int Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float HorizontalResolution { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.Imaging.ColorPalette Palette { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.SizeF PhysicalDimension { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.PixelFormat PixelFormat { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public int[] PropertyIdList { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + public System.Drawing.Imaging.PropertyItem[] PropertyItems { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.ImageFormat RawFormat { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Size Size { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DefaultValueAttribute(null)] + [System.ComponentModel.LocalizableAttribute(false)] + public object? Tag { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float VerticalResolution { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + [System.ComponentModel.DefaultValueAttribute(false)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public int Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + ~Image() { } + public static System.Drawing.Image FromFile(string filename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Image FromFile(string filename, bool useEmbeddedColorManagement) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Bitmap FromHbitmap(System.IntPtr hbitmap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Bitmap FromHbitmap(System.IntPtr hbitmap, System.IntPtr hpalette) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Image FromStream(System.IO.Stream stream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Image FromStream(System.IO.Stream stream, bool useEmbeddedColorManagement) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Image FromStream(System.IO.Stream stream, bool useEmbeddedColorManagement, bool validateImageData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF GetBounds(ref System.Drawing.GraphicsUnit pageUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.EncoderParameters? GetEncoderParameterList(System.Guid encoder) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int GetFrameCount(System.Drawing.Imaging.FrameDimension dimension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static int GetPixelFormatSize(System.Drawing.Imaging.PixelFormat pixfmt) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.PropertyItem? GetPropertyItem(int propid) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image GetThumbnailImage(int thumbWidth, int thumbHeight, System.Drawing.Image.GetThumbnailImageAbort? callback, System.IntPtr callbackData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool IsAlphaPixelFormat(System.Drawing.Imaging.PixelFormat pixfmt) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool IsCanonicalPixelFormat(System.Drawing.Imaging.PixelFormat pixfmt) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool IsExtendedPixelFormat(System.Drawing.Imaging.PixelFormat pixfmt) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RemovePropertyItem(int propid) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateFlip(System.Drawing.RotateFlipType rotateFlipType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(System.IO.Stream stream, System.Drawing.Imaging.ImageCodecInfo encoder, System.Drawing.Imaging.EncoderParameters? encoderParams) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(System.IO.Stream stream, System.Drawing.Imaging.ImageFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(string filename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(string filename, System.Drawing.Imaging.ImageCodecInfo encoder, System.Drawing.Imaging.EncoderParameters? encoderParams) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Save(string filename, System.Drawing.Imaging.ImageFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SaveAdd(System.Drawing.Image image, System.Drawing.Imaging.EncoderParameters? encoderParams) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SaveAdd(System.Drawing.Imaging.EncoderParameters? encoderParams) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int SelectActiveFrame(System.Drawing.Imaging.FrameDimension dimension, int frameIndex) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetPropertyItem(System.Drawing.Imaging.PropertyItem propitem) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo si, System.Runtime.Serialization.StreamingContext context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public delegate bool GetThumbnailImageAbort(); + } + public sealed partial class ImageAnimator + { + internal ImageAnimator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static void Animate(System.Drawing.Image image, System.EventHandler onFrameChangedHandler) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool CanAnimate([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] System.Drawing.Image? image) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static void StopAnimate(System.Drawing.Image image, System.EventHandler onFrameChangedHandler) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static void UpdateFrames() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static void UpdateFrames(System.Drawing.Image? image) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class ImageConverter : System.ComponentModel.TypeConverter + { + public ImageConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [Diagnostics.CodeAnalysis.RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + public override System.ComponentModel.PropertyDescriptorCollection GetProperties(System.ComponentModel.ITypeDescriptorContext? context, object? value, System.Attribute[]? attributes) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class ImageFormatConverter : System.ComponentModel.TypeConverter + { + public ImageFormatConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type? sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class Pen : System.MarshalByRefObject, System.ICloneable, System.IDisposable + { + public Pen(System.Drawing.Brush brush) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Pen(System.Drawing.Brush brush, float width) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Pen(System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Pen(System.Drawing.Color color, float width) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.PenAlignment Alignment { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Brush Brush { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color Color { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float[] CompoundArray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.CustomLineCap CustomEndCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.CustomLineCap CustomStartCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.DashCap DashCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float DashOffset { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float[] DashPattern { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.DashStyle DashStyle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.LineCap EndCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.LineJoin LineJoin { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float MiterLimit { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.PenType PenType { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.LineCap StartCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Transform { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + ~Pen() { } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetTransform() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetLineCap(System.Drawing.Drawing2D.LineCap startCap, System.Drawing.Drawing2D.LineCap endCap, System.Drawing.Drawing2D.DashCap dashCap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class Pens + { + public static System.Drawing.Pen AliceBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen AntiqueWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Aqua { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Aquamarine { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Azure { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Beige { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Bisque { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Black { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen BlanchedAlmond { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Blue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen BlueViolet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Brown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen BurlyWood { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen CadetBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Chartreuse { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Chocolate { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Coral { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen CornflowerBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Cornsilk { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Crimson { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Cyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkCyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkGoldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkKhaki { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkMagenta { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkOliveGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkOrange { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkOrchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkSalmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkSlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkSlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DarkViolet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DeepPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DeepSkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DimGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen DodgerBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Firebrick { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen FloralWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ForestGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Fuchsia { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Gainsboro { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen GhostWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Gold { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Goldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Gray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Green { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen GreenYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Honeydew { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen HotPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen IndianRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Indigo { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Ivory { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Khaki { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Lavender { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LavenderBlush { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LawnGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LemonChiffon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightCoral { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightCyan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightGoldenrodYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightPink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightSalmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightSkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightSlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightSteelBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LightYellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Lime { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen LimeGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Linen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Magenta { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Maroon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumAquamarine { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumOrchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumPurple { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumSeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumSlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumSpringGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MediumVioletRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MidnightBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MintCream { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MistyRose { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Moccasin { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen NavajoWhite { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Navy { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen OldLace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Olive { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen OliveDrab { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Orange { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen OrangeRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Orchid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PaleGoldenrod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PaleGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PaleTurquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PaleVioletRed { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PapayaWhip { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PeachPuff { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Peru { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Pink { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Plum { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen PowderBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Purple { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Red { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen RosyBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen RoyalBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SaddleBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Salmon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SandyBrown { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SeaGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SeaShell { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Sienna { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Silver { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SkyBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SlateBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SlateGray { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Snow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SpringGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen SteelBlue { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Tan { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Teal { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Thistle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Tomato { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Transparent { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Turquoise { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Violet { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Wheat { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen White { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen WhiteSmoke { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Yellow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen YellowGreen { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class Region : System.MarshalByRefObject, System.IDisposable + { + public Region() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Region(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Region(System.Drawing.Drawing2D.RegionData rgnData) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Region(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Region(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Region Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Complement(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Complement(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Complement(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Complement(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public bool Equals(System.Drawing.Region region, System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Exclude(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Exclude(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Exclude(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Exclude(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~Region() { } + public static System.Drawing.Region FromHrgn(System.IntPtr hrgn) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF GetBounds(System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHrgn(System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.RegionData? GetRegionData() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF[] GetRegionScans(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Intersect(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Intersect(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Intersect(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Intersect(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmpty(System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsInfinite(System.Drawing.Graphics g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Point point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Point point, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.PointF point, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Rectangle rect, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.RectangleF rect, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y, int width, int height, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y, float width, float height, System.Drawing.Graphics? g) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MakeEmpty() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MakeInfinite() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ReleaseHrgn(System.IntPtr regionHandle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Transform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Translate(int dx, int dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Translate(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Union(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Union(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Union(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Union(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Xor(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Xor(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Xor(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Xor(System.Drawing.Region region) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum RotateFlipType + { + Rotate180FlipXY = 0, + RotateNoneFlipNone = 0, + Rotate270FlipXY = 1, + Rotate90FlipNone = 1, + Rotate180FlipNone = 2, + RotateNoneFlipXY = 2, + Rotate270FlipNone = 3, + Rotate90FlipXY = 3, + Rotate180FlipY = 4, + RotateNoneFlipX = 4, + Rotate270FlipY = 5, + Rotate90FlipX = 5, + Rotate180FlipX = 6, + RotateNoneFlipY = 6, + Rotate270FlipX = 7, + Rotate90FlipY = 7, + } + public sealed partial class SolidBrush : System.Drawing.Brush + { + public SolidBrush(System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color Color { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected override void Dispose(bool disposing) { } + } + public enum StringAlignment + { + Near = 0, + Center = 1, + Far = 2, + } + public enum StringDigitSubstitute + { + User = 0, + None = 1, + National = 2, + Traditional = 3, + } + public sealed partial class StringFormat : System.MarshalByRefObject, System.ICloneable, System.IDisposable + { + public StringFormat() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public StringFormat(System.Drawing.StringFormat format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public StringFormat(System.Drawing.StringFormatFlags options) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public StringFormat(System.Drawing.StringFormatFlags options, int language) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.StringAlignment Alignment { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int DigitSubstitutionLanguage { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.StringDigitSubstitute DigitSubstitutionMethod { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.StringFormatFlags FormatFlags { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.StringFormat GenericDefault { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.StringFormat GenericTypographic { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Text.HotkeyPrefix HotkeyPrefix { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.StringAlignment LineAlignment { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.StringTrimming Trimming { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + ~StringFormat() { } + public float[] GetTabStops(out float firstTabOffset) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetDigitSubstitution(int language, System.Drawing.StringDigitSubstitute substitute) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetMeasurableCharacterRanges(System.Drawing.CharacterRange[] ranges) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetTabStops(float firstTabOffset, float[] tabStops) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.FlagsAttribute] + public enum StringFormatFlags + { + DirectionRightToLeft = 1, + DirectionVertical = 2, + FitBlackBox = 4, + DisplayFormatControl = 32, + NoFontFallback = 1024, + MeasureTrailingSpaces = 2048, + NoWrap = 4096, + LineLimit = 8192, + NoClip = 16384, + } + public enum StringTrimming + { + None = 0, + Character = 1, + Word = 2, + EllipsisCharacter = 3, + EllipsisWord = 4, + EllipsisPath = 5, + } + public enum StringUnit + { + World = 0, + Display = 1, + Pixel = 2, + Point = 3, + Inch = 4, + Document = 5, + Millimeter = 6, + Em = 32, + } + public static partial class SystemBrushes + { + public static System.Drawing.Brush ActiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ActiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush AppWorkspace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ButtonFace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ButtonHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ButtonShadow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Control { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ControlDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ControlDarkDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ControlLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ControlLightLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ControlText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Desktop { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush GradientActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush GradientInactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush GrayText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Highlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush HighlightText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush HotTrack { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush InactiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush InactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush InactiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Info { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush InfoText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Menu { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MenuBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MenuHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush MenuText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush ScrollBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush Window { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush WindowFrame { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush WindowText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Brush FromSystemColor(System.Drawing.Color c) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class SystemColors + { + public static System.Drawing.Color ActiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ActiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color AppWorkspace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ButtonFace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ButtonHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ButtonShadow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Control { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ControlDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ControlDarkDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ControlLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ControlLightLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ControlText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Desktop { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color GradientActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color GradientInactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color GrayText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Highlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color HighlightText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color HotTrack { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color InactiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color InactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color InactiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Info { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color InfoText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Menu { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color MenuBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color MenuHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color MenuText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color ScrollBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color Window { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color WindowFrame { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Color WindowText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public static partial class SystemFonts + { + public static System.Drawing.Font? CaptionFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font DefaultFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font DialogFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? IconTitleFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? MenuFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? MessageBoxFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? SmallCaptionFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? StatusFont { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Font? GetFontByName(string systemFontName) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public static partial class SystemIcons + { + public static System.Drawing.Icon Application { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Asterisk { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Error { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Exclamation { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Hand { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Information { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Question { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Shield { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon Warning { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Icon WinLogo { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public static partial class SystemPens + { + public static System.Drawing.Pen ActiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ActiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen AppWorkspace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ButtonFace { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ButtonHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ButtonShadow { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Control { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ControlDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ControlDarkDark { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ControlLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ControlLightLight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ControlText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Desktop { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen GradientActiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen GradientInactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen GrayText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Highlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen HighlightText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen HotTrack { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen InactiveBorder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen InactiveCaption { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen InactiveCaptionText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Info { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen InfoText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Menu { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MenuBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MenuHighlight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen MenuText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen ScrollBar { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen Window { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen WindowFrame { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen WindowText { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Pen FromSystemColor(System.Drawing.Color c) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class TextureBrush : System.Drawing.Brush + { + public TextureBrush(System.Drawing.Image bitmap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.Drawing2D.WrapMode wrapMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.Drawing2D.WrapMode wrapMode, System.Drawing.Rectangle dstRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.Drawing2D.WrapMode wrapMode, System.Drawing.RectangleF dstRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.Rectangle dstRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.Rectangle dstRect, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.RectangleF dstRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public TextureBrush(System.Drawing.Image image, System.Drawing.RectangleF dstRect, System.Drawing.Imaging.ImageAttributes? imageAttr) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image Image { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Transform { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.WrapMode WrapMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetTransform() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.AttributeUsageAttribute(System.AttributeTargets.Class)] + public partial class ToolboxBitmapAttribute : System.Attribute + { + public static readonly System.Drawing.ToolboxBitmapAttribute Default; + public ToolboxBitmapAttribute(string imageFile) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public ToolboxBitmapAttribute(System.Type t) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public ToolboxBitmapAttribute(System.Type t, string name) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image? GetImage(object? component) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image? GetImage(object? component, bool large) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image? GetImage(System.Type type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image? GetImage(System.Type type, bool large) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image? GetImage(System.Type type, string? imgName, bool large) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Image? GetImageFromResource(System.Type t, string? imageName, bool large) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } +} +namespace System.Drawing.Design +{ + public sealed partial class CategoryNameCollection : System.Collections.ReadOnlyCollectionBase + { + public CategoryNameCollection(System.Drawing.Design.CategoryNameCollection value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public CategoryNameCollection(string[] value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public string this[int index] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool Contains(string value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyTo(string[] array, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int IndexOf(string value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } +} +namespace System.Drawing.Drawing2D +{ + public sealed partial class AdjustableArrowCap : System.Drawing.Drawing2D.CustomLineCap + { + public AdjustableArrowCap(float width, float height) : base (default(System.Drawing.Drawing2D.GraphicsPath), default(System.Drawing.Drawing2D.GraphicsPath)) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public AdjustableArrowCap(float width, float height, bool isFilled) : base (default(System.Drawing.Drawing2D.GraphicsPath), default(System.Drawing.Drawing2D.GraphicsPath)) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool Filled { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float MiddleInset { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class Blend + { + public Blend() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Blend(int count) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float[] Factors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float[] Positions { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class ColorBlend + { + public ColorBlend() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public ColorBlend(int count) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color[] Colors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float[] Positions { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum CombineMode + { + Replace = 0, + Intersect = 1, + Union = 2, + Xor = 3, + Exclude = 4, + Complement = 5, + } + public enum CompositingMode + { + SourceOver = 0, + SourceCopy = 1, + } + public enum CompositingQuality + { + Invalid = -1, + Default = 0, + HighSpeed = 1, + HighQuality = 2, + GammaCorrected = 3, + AssumeLinear = 4, + } + public enum CoordinateSpace + { + World = 0, + Page = 1, + Device = 2, + } + public partial class CustomLineCap : System.MarshalByRefObject, System.ICloneable, System.IDisposable + { + public CustomLineCap(System.Drawing.Drawing2D.GraphicsPath? fillPath, System.Drawing.Drawing2D.GraphicsPath? strokePath) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public CustomLineCap(System.Drawing.Drawing2D.GraphicsPath? fillPath, System.Drawing.Drawing2D.GraphicsPath? strokePath, System.Drawing.Drawing2D.LineCap baseCap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public CustomLineCap(System.Drawing.Drawing2D.GraphicsPath? fillPath, System.Drawing.Drawing2D.GraphicsPath? strokePath, System.Drawing.Drawing2D.LineCap baseCap, float baseInset) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.LineCap BaseCap { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float BaseInset { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.LineJoin StrokeJoin { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float WidthScale { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + ~CustomLineCap() { } + public void GetStrokeCaps(out System.Drawing.Drawing2D.LineCap startCap, out System.Drawing.Drawing2D.LineCap endCap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetStrokeCaps(System.Drawing.Drawing2D.LineCap startCap, System.Drawing.Drawing2D.LineCap endCap) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum DashCap + { + Flat = 0, + Round = 2, + Triangle = 3, + } + public enum DashStyle + { + Solid = 0, + Dash = 1, + Dot = 2, + DashDot = 3, + DashDotDot = 4, + Custom = 5, + } + public enum FillMode + { + Alternate = 0, + Winding = 1, + } + public enum FlushIntention + { + Flush = 0, + Sync = 1, + } + public sealed partial class GraphicsContainer : System.MarshalByRefObject + { + internal GraphicsContainer() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class GraphicsPath : System.MarshalByRefObject, System.ICloneable, System.IDisposable + { + public GraphicsPath() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public GraphicsPath(System.Drawing.Drawing2D.FillMode fillMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public GraphicsPath(System.Drawing.PointF[] pts, byte[] types) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public GraphicsPath(System.Drawing.PointF[] pts, byte[] types, System.Drawing.Drawing2D.FillMode fillMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public GraphicsPath(System.Drawing.Point[] pts, byte[] types) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public GraphicsPath(System.Drawing.Point[] pts, byte[] types, System.Drawing.Drawing2D.FillMode fillMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.FillMode FillMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.PathData PathData { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.PointF[] PathPoints { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public byte[] PathTypes { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int PointCount { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void AddArc(System.Drawing.Rectangle rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddArc(System.Drawing.RectangleF rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddArc(int x, int y, int width, int height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddArc(float x, float y, float width, float height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBezier(System.Drawing.Point pt1, System.Drawing.Point pt2, System.Drawing.Point pt3, System.Drawing.Point pt4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBezier(System.Drawing.PointF pt1, System.Drawing.PointF pt2, System.Drawing.PointF pt3, System.Drawing.PointF pt4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBezier(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBezier(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBeziers(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddBeziers(params System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddClosedCurve(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddClosedCurve(System.Drawing.PointF[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddClosedCurve(System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddClosedCurve(System.Drawing.Point[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.PointF[] points, int offset, int numberOfSegments, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.PointF[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.Point[] points, int offset, int numberOfSegments, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddCurve(System.Drawing.Point[] points, float tension) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddEllipse(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddEllipse(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddEllipse(int x, int y, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddEllipse(float x, float y, float width, float height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLine(System.Drawing.Point pt1, System.Drawing.Point pt2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLine(System.Drawing.PointF pt1, System.Drawing.PointF pt2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLine(int x1, int y1, int x2, int y2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLine(float x1, float y1, float x2, float y2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLines(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddLines(System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPath(System.Drawing.Drawing2D.GraphicsPath addingPath, bool connect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPie(System.Drawing.Rectangle rect, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPie(int x, int y, int width, int height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPie(float x, float y, float width, float height, float startAngle, float sweepAngle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPolygon(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddPolygon(System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddRectangle(System.Drawing.Rectangle rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddRectangle(System.Drawing.RectangleF rect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddRectangles(System.Drawing.RectangleF[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddRectangles(System.Drawing.Rectangle[] rects) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Point origin, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.PointF origin, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.Rectangle layoutRect, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddString(string s, System.Drawing.FontFamily family, int style, float emSize, System.Drawing.RectangleF layoutRect, System.Drawing.StringFormat? format) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearMarkers() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CloseAllFigures() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CloseFigure() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + ~GraphicsPath() { } + public void Flatten() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Flatten(System.Drawing.Drawing2D.Matrix? matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Flatten(System.Drawing.Drawing2D.Matrix? matrix, float flatness) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF GetBounds() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF GetBounds(System.Drawing.Drawing2D.Matrix? matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.RectangleF GetBounds(System.Drawing.Drawing2D.Matrix? matrix, System.Drawing.Pen? pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.PointF GetLastPoint() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(System.Drawing.Point point, System.Drawing.Pen pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(System.Drawing.Point pt, System.Drawing.Pen pen, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(System.Drawing.PointF point, System.Drawing.Pen pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(System.Drawing.PointF pt, System.Drawing.Pen pen, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(int x, int y, System.Drawing.Pen pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(int x, int y, System.Drawing.Pen pen, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(float x, float y, System.Drawing.Pen pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsOutlineVisible(float x, float y, System.Drawing.Pen pen, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Point point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.Point pt, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(System.Drawing.PointF pt, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(int x, int y, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsVisible(float x, float y, System.Drawing.Graphics? graphics) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Reset() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Reverse() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetMarkers() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void StartFigure() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Transform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Warp(System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Warp(System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.Drawing2D.Matrix? matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Warp(System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.Drawing2D.Matrix? matrix, System.Drawing.Drawing2D.WarpMode warpMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Warp(System.Drawing.PointF[] destPoints, System.Drawing.RectangleF srcRect, System.Drawing.Drawing2D.Matrix? matrix, System.Drawing.Drawing2D.WarpMode warpMode, float flatness) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Widen(System.Drawing.Pen pen) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Widen(System.Drawing.Pen pen, System.Drawing.Drawing2D.Matrix? matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Widen(System.Drawing.Pen pen, System.Drawing.Drawing2D.Matrix? matrix, float flatness) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class GraphicsPathIterator : System.MarshalByRefObject, System.IDisposable + { + public GraphicsPathIterator(System.Drawing.Drawing2D.GraphicsPath? path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int SubpathCount { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int CopyData(ref System.Drawing.PointF[] points, ref byte[] types, int startIndex, int endIndex) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public int Enumerate(ref System.Drawing.PointF[] points, ref byte[] types) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~GraphicsPathIterator() { } + public bool HasCurve() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int NextMarker(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int NextMarker(out int startIndex, out int endIndex) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int NextPathType(out byte pathType, out int startIndex, out int endIndex) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int NextSubpath(System.Drawing.Drawing2D.GraphicsPath path, out bool isClosed) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int NextSubpath(out int startIndex, out int endIndex, out bool isClosed) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Rewind() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class GraphicsState : System.MarshalByRefObject + { + internal GraphicsState() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class HatchBrush : System.Drawing.Brush + { + public HatchBrush(System.Drawing.Drawing2D.HatchStyle hatchstyle, System.Drawing.Color foreColor) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public HatchBrush(System.Drawing.Drawing2D.HatchStyle hatchstyle, System.Drawing.Color foreColor, System.Drawing.Color backColor) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color BackgroundColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color ForegroundColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.HatchStyle HatchStyle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum HatchStyle + { + Horizontal = 0, + Min = 0, + Vertical = 1, + ForwardDiagonal = 2, + BackwardDiagonal = 3, + Cross = 4, + LargeGrid = 4, + Max = 4, + DiagonalCross = 5, + Percent05 = 6, + Percent10 = 7, + Percent20 = 8, + Percent25 = 9, + Percent30 = 10, + Percent40 = 11, + Percent50 = 12, + Percent60 = 13, + Percent70 = 14, + Percent75 = 15, + Percent80 = 16, + Percent90 = 17, + LightDownwardDiagonal = 18, + LightUpwardDiagonal = 19, + DarkDownwardDiagonal = 20, + DarkUpwardDiagonal = 21, + WideDownwardDiagonal = 22, + WideUpwardDiagonal = 23, + LightVertical = 24, + LightHorizontal = 25, + NarrowVertical = 26, + NarrowHorizontal = 27, + DarkVertical = 28, + DarkHorizontal = 29, + DashedDownwardDiagonal = 30, + DashedUpwardDiagonal = 31, + DashedHorizontal = 32, + DashedVertical = 33, + SmallConfetti = 34, + LargeConfetti = 35, + ZigZag = 36, + Wave = 37, + DiagonalBrick = 38, + HorizontalBrick = 39, + Weave = 40, + Plaid = 41, + Divot = 42, + DottedGrid = 43, + DottedDiamond = 44, + Shingle = 45, + Trellis = 46, + Sphere = 47, + SmallGrid = 48, + SmallCheckerBoard = 49, + LargeCheckerBoard = 50, + OutlinedDiamond = 51, + SolidDiamond = 52, + } + public enum InterpolationMode + { + Invalid = -1, + Default = 0, + Low = 1, + High = 2, + Bilinear = 3, + Bicubic = 4, + NearestNeighbor = 5, + HighQualityBilinear = 6, + HighQualityBicubic = 7, + } + public sealed partial class LinearGradientBrush : System.Drawing.Brush + { + public LinearGradientBrush(System.Drawing.Point point1, System.Drawing.Point point2, System.Drawing.Color color1, System.Drawing.Color color2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.PointF point1, System.Drawing.PointF point2, System.Drawing.Color color1, System.Drawing.Color color2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.Rectangle rect, System.Drawing.Color color1, System.Drawing.Color color2, System.Drawing.Drawing2D.LinearGradientMode linearGradientMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.Rectangle rect, System.Drawing.Color color1, System.Drawing.Color color2, float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.Rectangle rect, System.Drawing.Color color1, System.Drawing.Color color2, float angle, bool isAngleScaleable) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.RectangleF rect, System.Drawing.Color color1, System.Drawing.Color color2, System.Drawing.Drawing2D.LinearGradientMode linearGradientMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.RectangleF rect, System.Drawing.Color color1, System.Drawing.Color color2, float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public LinearGradientBrush(System.Drawing.RectangleF rect, System.Drawing.Color color1, System.Drawing.Color color2, float angle, bool isAngleScaleable) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.Blend? Blend { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool GammaCorrection { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.ColorBlend InterpolationColors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color[] LinearColors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.RectangleF Rectangle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Transform { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.WrapMode WrapMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetTransform() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetBlendTriangularShape(float focus) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetBlendTriangularShape(float focus, float scale) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetSigmaBellShape(float focus) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetSigmaBellShape(float focus, float scale) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum LinearGradientMode + { + Horizontal = 0, + Vertical = 1, + ForwardDiagonal = 2, + BackwardDiagonal = 3, + } + public enum LineCap + { + Flat = 0, + Square = 1, + Round = 2, + Triangle = 3, + NoAnchor = 16, + SquareAnchor = 17, + RoundAnchor = 18, + DiamondAnchor = 19, + ArrowAnchor = 20, + AnchorMask = 240, + Custom = 255, + } + public enum LineJoin + { + Miter = 0, + Bevel = 1, + Round = 2, + MiterClipped = 3, + } + public sealed partial class Matrix : System.MarshalByRefObject, System.IDisposable + { + public Matrix() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Matrix(System.Drawing.Rectangle rect, System.Drawing.Point[] plgpts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Matrix(System.Drawing.RectangleF rect, System.Drawing.PointF[] plgpts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Matrix(float m11, float m12, float m21, float m22, float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float[] Elements { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsIdentity { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsInvertible { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float OffsetX { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float OffsetY { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + ~Matrix() { } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Invert() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Multiply(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Multiply(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Reset() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Rotate(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Rotate(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateAt(float angle, System.Drawing.PointF point) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateAt(float angle, System.Drawing.PointF point, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Scale(float scaleX, float scaleY) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Scale(float scaleX, float scaleY, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Shear(float shearX, float shearY) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Shear(float shearX, float shearY, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformPoints(System.Drawing.PointF[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformPoints(System.Drawing.Point[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformVectors(System.Drawing.PointF[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TransformVectors(System.Drawing.Point[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Translate(float offsetX, float offsetY) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Translate(float offsetX, float offsetY, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void VectorTransformPoints(System.Drawing.Point[] pts) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum MatrixOrder + { + Prepend = 0, + Append = 1, + } + public sealed partial class PathData + { + public PathData() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.PointF[]? Points { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public byte[]? Types { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class PathGradientBrush : System.Drawing.Brush + { + public PathGradientBrush(System.Drawing.Drawing2D.GraphicsPath path) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PathGradientBrush(System.Drawing.PointF[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PathGradientBrush(System.Drawing.PointF[] points, System.Drawing.Drawing2D.WrapMode wrapMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PathGradientBrush(System.Drawing.Point[] points) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PathGradientBrush(System.Drawing.Point[] points, System.Drawing.Drawing2D.WrapMode wrapMode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Drawing2D.Blend Blend { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color CenterColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.PointF CenterPoint { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.PointF FocusScales { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.ColorBlend InterpolationColors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.RectangleF Rectangle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color[] SurroundColors { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.Matrix Transform { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Drawing2D.WrapMode WrapMode { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void MultiplyTransform(System.Drawing.Drawing2D.Matrix matrix, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ResetTransform() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void RotateTransform(float angle, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ScaleTransform(float sx, float sy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetBlendTriangularShape(float focus) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetBlendTriangularShape(float focus, float scale) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetSigmaBellShape(float focus) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetSigmaBellShape(float focus, float scale) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void TranslateTransform(float dx, float dy, System.Drawing.Drawing2D.MatrixOrder order) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum PathPointType + { + Start = 0, + Line = 1, + Bezier = 3, + Bezier3 = 3, + PathTypeMask = 7, + DashMode = 16, + PathMarker = 32, + CloseSubpath = 128, + } + public enum PenAlignment + { + Center = 0, + Inset = 1, + Outset = 2, + Left = 3, + Right = 4, + } + public enum PenType + { + SolidColor = 0, + HatchFill = 1, + TextureFill = 2, + PathGradient = 3, + LinearGradient = 4, + } + public enum PixelOffsetMode + { + Invalid = -1, + Default = 0, + HighSpeed = 1, + HighQuality = 2, + None = 3, + Half = 4, + } + public enum QualityMode + { + Invalid = -1, + Default = 0, + Low = 1, + High = 2, + } + public sealed partial class RegionData + { + internal RegionData() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public byte[] Data { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum SmoothingMode + { + Invalid = -1, + Default = 0, + HighSpeed = 1, + HighQuality = 2, + None = 3, + AntiAlias = 4, + } + public enum WarpMode + { + Perspective = 0, + Bilinear = 1, + } + public enum WrapMode + { + Tile = 0, + TileFlipX = 1, + TileFlipY = 2, + TileFlipXY = 3, + Clamp = 4, + } +} +namespace System.Drawing.Imaging +{ + public sealed partial class BitmapData + { + public BitmapData() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.PixelFormat PixelFormat { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Reserved { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.IntPtr Scan0 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Stride { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum ColorAdjustType + { + Default = 0, + Bitmap = 1, + Brush = 2, + Pen = 3, + Text = 4, + Count = 5, + Any = 6, + } + public enum ColorChannelFlag + { + ColorChannelC = 0, + ColorChannelM = 1, + ColorChannelY = 2, + ColorChannelK = 3, + ColorChannelLast = 4, + } + public sealed partial class ColorMap + { + public ColorMap() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color NewColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Color OldColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum ColorMapType + { + Default = 0, + Brush = 1, + } + public sealed partial class ColorMatrix + { + public ColorMatrix() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.CLSCompliantAttribute(false)] + public ColorMatrix(float[][] newColorMatrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public float this[int row, int column] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix00 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix01 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix02 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix03 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix04 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix10 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix11 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix12 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix13 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix14 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix20 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix21 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix22 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix23 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix24 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix30 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix31 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix32 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix33 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix34 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix40 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix41 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix42 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix43 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float Matrix44 { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum ColorMatrixFlag + { + Default = 0, + SkipGrays = 1, + AltGrays = 2, + } + public enum ColorMode + { + Argb32Mode = 0, + Argb64Mode = 1, + } + public sealed partial class ColorPalette + { + internal ColorPalette() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Color[] Entries { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Flags { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public enum EmfPlusRecordType + { + EmfHeader = 1, + EmfMin = 1, + EmfPolyBezier = 2, + EmfPolygon = 3, + EmfPolyline = 4, + EmfPolyBezierTo = 5, + EmfPolyLineTo = 6, + EmfPolyPolyline = 7, + EmfPolyPolygon = 8, + EmfSetWindowExtEx = 9, + EmfSetWindowOrgEx = 10, + EmfSetViewportExtEx = 11, + EmfSetViewportOrgEx = 12, + EmfSetBrushOrgEx = 13, + EmfEof = 14, + EmfSetPixelV = 15, + EmfSetMapperFlags = 16, + EmfSetMapMode = 17, + EmfSetBkMode = 18, + EmfSetPolyFillMode = 19, + EmfSetROP2 = 20, + EmfSetStretchBltMode = 21, + EmfSetTextAlign = 22, + EmfSetColorAdjustment = 23, + EmfSetTextColor = 24, + EmfSetBkColor = 25, + EmfOffsetClipRgn = 26, + EmfMoveToEx = 27, + EmfSetMetaRgn = 28, + EmfExcludeClipRect = 29, + EmfIntersectClipRect = 30, + EmfScaleViewportExtEx = 31, + EmfScaleWindowExtEx = 32, + EmfSaveDC = 33, + EmfRestoreDC = 34, + EmfSetWorldTransform = 35, + EmfModifyWorldTransform = 36, + EmfSelectObject = 37, + EmfCreatePen = 38, + EmfCreateBrushIndirect = 39, + EmfDeleteObject = 40, + EmfAngleArc = 41, + EmfEllipse = 42, + EmfRectangle = 43, + EmfRoundRect = 44, + EmfRoundArc = 45, + EmfChord = 46, + EmfPie = 47, + EmfSelectPalette = 48, + EmfCreatePalette = 49, + EmfSetPaletteEntries = 50, + EmfResizePalette = 51, + EmfRealizePalette = 52, + EmfExtFloodFill = 53, + EmfLineTo = 54, + EmfArcTo = 55, + EmfPolyDraw = 56, + EmfSetArcDirection = 57, + EmfSetMiterLimit = 58, + EmfBeginPath = 59, + EmfEndPath = 60, + EmfCloseFigure = 61, + EmfFillPath = 62, + EmfStrokeAndFillPath = 63, + EmfStrokePath = 64, + EmfFlattenPath = 65, + EmfWidenPath = 66, + EmfSelectClipPath = 67, + EmfAbortPath = 68, + EmfReserved069 = 69, + EmfGdiComment = 70, + EmfFillRgn = 71, + EmfFrameRgn = 72, + EmfInvertRgn = 73, + EmfPaintRgn = 74, + EmfExtSelectClipRgn = 75, + EmfBitBlt = 76, + EmfStretchBlt = 77, + EmfMaskBlt = 78, + EmfPlgBlt = 79, + EmfSetDIBitsToDevice = 80, + EmfStretchDIBits = 81, + EmfExtCreateFontIndirect = 82, + EmfExtTextOutA = 83, + EmfExtTextOutW = 84, + EmfPolyBezier16 = 85, + EmfPolygon16 = 86, + EmfPolyline16 = 87, + EmfPolyBezierTo16 = 88, + EmfPolylineTo16 = 89, + EmfPolyPolyline16 = 90, + EmfPolyPolygon16 = 91, + EmfPolyDraw16 = 92, + EmfCreateMonoBrush = 93, + EmfCreateDibPatternBrushPt = 94, + EmfExtCreatePen = 95, + EmfPolyTextOutA = 96, + EmfPolyTextOutW = 97, + EmfSetIcmMode = 98, + EmfCreateColorSpace = 99, + EmfSetColorSpace = 100, + EmfDeleteColorSpace = 101, + EmfGlsRecord = 102, + EmfGlsBoundedRecord = 103, + EmfPixelFormat = 104, + EmfDrawEscape = 105, + EmfExtEscape = 106, + EmfStartDoc = 107, + EmfSmallTextOut = 108, + EmfForceUfiMapping = 109, + EmfNamedEscpae = 110, + EmfColorCorrectPalette = 111, + EmfSetIcmProfileA = 112, + EmfSetIcmProfileW = 113, + EmfAlphaBlend = 114, + EmfSetLayout = 115, + EmfTransparentBlt = 116, + EmfReserved117 = 117, + EmfGradientFill = 118, + EmfSetLinkedUfis = 119, + EmfSetTextJustification = 120, + EmfColorMatchToTargetW = 121, + EmfCreateColorSpaceW = 122, + EmfMax = 122, + EmfPlusRecordBase = 16384, + Invalid = 16384, + Header = 16385, + Min = 16385, + EndOfFile = 16386, + Comment = 16387, + GetDC = 16388, + MultiFormatStart = 16389, + MultiFormatSection = 16390, + MultiFormatEnd = 16391, + Object = 16392, + Clear = 16393, + FillRects = 16394, + DrawRects = 16395, + FillPolygon = 16396, + DrawLines = 16397, + FillEllipse = 16398, + DrawEllipse = 16399, + FillPie = 16400, + DrawPie = 16401, + DrawArc = 16402, + FillRegion = 16403, + FillPath = 16404, + DrawPath = 16405, + FillClosedCurve = 16406, + DrawClosedCurve = 16407, + DrawCurve = 16408, + DrawBeziers = 16409, + DrawImage = 16410, + DrawImagePoints = 16411, + DrawString = 16412, + SetRenderingOrigin = 16413, + SetAntiAliasMode = 16414, + SetTextRenderingHint = 16415, + SetTextContrast = 16416, + SetInterpolationMode = 16417, + SetPixelOffsetMode = 16418, + SetCompositingMode = 16419, + SetCompositingQuality = 16420, + Save = 16421, + Restore = 16422, + BeginContainer = 16423, + BeginContainerNoParams = 16424, + EndContainer = 16425, + SetWorldTransform = 16426, + ResetWorldTransform = 16427, + MultiplyWorldTransform = 16428, + TranslateWorldTransform = 16429, + ScaleWorldTransform = 16430, + RotateWorldTransform = 16431, + SetPageTransform = 16432, + ResetClip = 16433, + SetClipRect = 16434, + SetClipPath = 16435, + SetClipRegion = 16436, + OffsetClip = 16437, + DrawDriverString = 16438, + Max = 16438, + Total = 16439, + WmfRecordBase = 65536, + WmfSaveDC = 65566, + WmfRealizePalette = 65589, + WmfSetPalEntries = 65591, + WmfCreatePalette = 65783, + WmfSetBkMode = 65794, + WmfSetMapMode = 65795, + WmfSetROP2 = 65796, + WmfSetRelAbs = 65797, + WmfSetPolyFillMode = 65798, + WmfSetStretchBltMode = 65799, + WmfSetTextCharExtra = 65800, + WmfRestoreDC = 65831, + WmfInvertRegion = 65834, + WmfPaintRegion = 65835, + WmfSelectClipRegion = 65836, + WmfSelectObject = 65837, + WmfSetTextAlign = 65838, + WmfResizePalette = 65849, + WmfDibCreatePatternBrush = 65858, + WmfSetLayout = 65865, + WmfDeleteObject = 66032, + WmfCreatePatternBrush = 66041, + WmfSetBkColor = 66049, + WmfSetTextColor = 66057, + WmfSetTextJustification = 66058, + WmfSetWindowOrg = 66059, + WmfSetWindowExt = 66060, + WmfSetViewportOrg = 66061, + WmfSetViewportExt = 66062, + WmfOffsetWindowOrg = 66063, + WmfOffsetViewportOrg = 66065, + WmfLineTo = 66067, + WmfMoveTo = 66068, + WmfOffsetCilpRgn = 66080, + WmfFillRegion = 66088, + WmfSetMapperFlags = 66097, + WmfSelectPalette = 66100, + WmfCreatePenIndirect = 66298, + WmfCreateFontIndirect = 66299, + WmfCreateBrushIndirect = 66300, + WmfPolygon = 66340, + WmfPolyline = 66341, + WmfScaleWindowExt = 66576, + WmfScaleViewportExt = 66578, + WmfExcludeClipRect = 66581, + WmfIntersectClipRect = 66582, + WmfEllipse = 66584, + WmfFloodFill = 66585, + WmfRectangle = 66587, + WmfSetPixel = 66591, + WmfFrameRegion = 66601, + WmfAnimatePalette = 66614, + WmfTextOut = 66849, + WmfPolyPolygon = 66872, + WmfExtFloodFill = 66888, + WmfRoundRect = 67100, + WmfPatBlt = 67101, + WmfEscape = 67110, + WmfCreateRegion = 67327, + WmfArc = 67607, + WmfPie = 67610, + WmfChord = 67632, + WmfBitBlt = 67874, + WmfDibBitBlt = 67904, + WmfExtTextOut = 68146, + WmfStretchBlt = 68387, + WmfDibStretchBlt = 68417, + WmfSetDibToDev = 68915, + WmfStretchDib = 69443, + } + public enum EmfType + { + EmfOnly = 3, + EmfPlusOnly = 4, + EmfPlusDual = 5, + } + public sealed partial class Encoder + { + public static readonly System.Drawing.Imaging.Encoder ChrominanceTable; + public static readonly System.Drawing.Imaging.Encoder ColorDepth; + public static readonly System.Drawing.Imaging.Encoder ColorSpace; + public static readonly System.Drawing.Imaging.Encoder Compression; + public static readonly System.Drawing.Imaging.Encoder ImageItems; + public static readonly System.Drawing.Imaging.Encoder LuminanceTable; + public static readonly System.Drawing.Imaging.Encoder Quality; + public static readonly System.Drawing.Imaging.Encoder RenderMethod; + public static readonly System.Drawing.Imaging.Encoder SaveAsCmyk; + public static readonly System.Drawing.Imaging.Encoder SaveFlag; + public static readonly System.Drawing.Imaging.Encoder ScanMethod; + public static readonly System.Drawing.Imaging.Encoder Transformation; + public static readonly System.Drawing.Imaging.Encoder Version; + public Encoder(System.Guid guid) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Guid Guid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class EncoderParameter : System.IDisposable + { + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, byte value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, byte value, bool undefined) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, byte[] value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, byte[] value, bool undefined) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, short value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, short[] value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int numberValues, System.Drawing.Imaging.EncoderParameterValueType type, System.IntPtr value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int numerator, int denominator) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ObsoleteAttribute("This constructor has been deprecated. Use EncoderParameter(Encoder encoder, int numberValues, EncoderParameterValueType type, IntPtr value) instead.")] + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int NumberOfValues, int Type, int Value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int numerator1, int demoninator1, int numerator2, int demoninator2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int[] numerator, int[] denominator) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, int[] numerator1, int[] denominator1, int[] numerator2, int[] denominator2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, long value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, long rangebegin, long rangeend) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, long[] value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, long[] rangebegin, long[] rangeend) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameter(System.Drawing.Imaging.Encoder encoder, string value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.Encoder Encoder { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int NumberOfValues { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.EncoderParameterValueType Type { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.EncoderParameterValueType ValueType { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void Dispose() { } + ~EncoderParameter() { } + } + public sealed partial class EncoderParameters : System.IDisposable + { + public EncoderParameters() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public EncoderParameters(int count) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.EncoderParameter[] Param { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void Dispose() { } + } + public enum EncoderParameterValueType + { + ValueTypeByte = 1, + ValueTypeAscii = 2, + ValueTypeShort = 3, + ValueTypeLong = 4, + ValueTypeRational = 5, + ValueTypeLongRange = 6, + ValueTypeUndefined = 7, + ValueTypeRationalRange = 8, + ValueTypePointer = 9, + } + public enum EncoderValue + { + ColorTypeCMYK = 0, + ColorTypeYCCK = 1, + CompressionLZW = 2, + CompressionCCITT3 = 3, + CompressionCCITT4 = 4, + CompressionRle = 5, + CompressionNone = 6, + ScanMethodInterlaced = 7, + ScanMethodNonInterlaced = 8, + VersionGif87 = 9, + VersionGif89 = 10, + RenderProgressive = 11, + RenderNonProgressive = 12, + TransformRotate90 = 13, + TransformRotate180 = 14, + TransformRotate270 = 15, + TransformFlipHorizontal = 16, + TransformFlipVertical = 17, + MultiFrame = 18, + LastFrame = 19, + Flush = 20, + FrameDimensionTime = 21, + FrameDimensionResolution = 22, + FrameDimensionPage = 23, + } + public sealed partial class FrameDimension + { + public FrameDimension(System.Guid guid) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Guid Guid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.FrameDimension Page { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.FrameDimension Resolution { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.FrameDimension Time { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? o) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class ImageAttributes : System.ICloneable, System.IDisposable + { + public ImageAttributes() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearBrushRemapTable() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearColorKey() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearColorKey(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearColorMatrix() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearColorMatrix(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearGamma() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearGamma(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearNoOp() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearNoOp(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearOutputChannel() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearOutputChannel(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearOutputChannelColorProfile() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearOutputChannelColorProfile(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearRemapTable() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearRemapTable(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearThreshold() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void ClearThreshold(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Dispose() { } + ~ImageAttributes() { } + public void GetAdjustedPalette(System.Drawing.Imaging.ColorPalette palette, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetBrushRemapTable(System.Drawing.Imaging.ColorMap[] map) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorKey(System.Drawing.Color colorLow, System.Drawing.Color colorHigh) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorKey(System.Drawing.Color colorLow, System.Drawing.Color colorHigh, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrices(System.Drawing.Imaging.ColorMatrix newColorMatrix, System.Drawing.Imaging.ColorMatrix? grayMatrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrices(System.Drawing.Imaging.ColorMatrix newColorMatrix, System.Drawing.Imaging.ColorMatrix? grayMatrix, System.Drawing.Imaging.ColorMatrixFlag flags) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrices(System.Drawing.Imaging.ColorMatrix newColorMatrix, System.Drawing.Imaging.ColorMatrix? grayMatrix, System.Drawing.Imaging.ColorMatrixFlag mode, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrix(System.Drawing.Imaging.ColorMatrix newColorMatrix) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrix(System.Drawing.Imaging.ColorMatrix newColorMatrix, System.Drawing.Imaging.ColorMatrixFlag flags) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetColorMatrix(System.Drawing.Imaging.ColorMatrix newColorMatrix, System.Drawing.Imaging.ColorMatrixFlag mode, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetGamma(float gamma) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetGamma(float gamma, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetNoOp() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetNoOp(System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetOutputChannel(System.Drawing.Imaging.ColorChannelFlag flags) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetOutputChannel(System.Drawing.Imaging.ColorChannelFlag flags, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetOutputChannelColorProfile(string colorProfileFilename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetOutputChannelColorProfile(string colorProfileFilename, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetRemapTable(System.Drawing.Imaging.ColorMap[] map) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetRemapTable(System.Drawing.Imaging.ColorMap[] map, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetThreshold(float threshold) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetThreshold(float threshold, System.Drawing.Imaging.ColorAdjustType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetWrapMode(System.Drawing.Drawing2D.WrapMode mode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetWrapMode(System.Drawing.Drawing2D.WrapMode mode, System.Drawing.Color color) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetWrapMode(System.Drawing.Drawing2D.WrapMode mode, System.Drawing.Color color, bool clamp) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.FlagsAttribute] + public enum ImageCodecFlags + { + Encoder = 1, + Decoder = 2, + SupportBitmap = 4, + SupportVector = 8, + SeekableEncode = 16, + BlockingDecode = 32, + Builtin = 65536, + System = 131072, + User = 262144, + } + public sealed partial class ImageCodecInfo + { + internal ImageCodecInfo() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Guid Clsid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string? CodecName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string? DllName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string? FilenameExtension { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.ImageCodecFlags Flags { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string? FormatDescription { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Guid FormatID { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string? MimeType { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.CLSCompliantAttribute(false)] + public byte[][]? SignatureMasks { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.CLSCompliantAttribute(false)] + public byte[][]? SignaturePatterns { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Version { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageCodecInfo[] GetImageDecoders() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.ImageCodecInfo[] GetImageEncoders() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.FlagsAttribute] + public enum ImageFlags + { + None = 0, + Scalable = 1, + HasAlpha = 2, + HasTranslucent = 4, + PartiallyScalable = 8, + ColorSpaceRgb = 16, + ColorSpaceCmyk = 32, + ColorSpaceGray = 64, + ColorSpaceYcbcr = 128, + ColorSpaceYcck = 256, + HasRealDpi = 4096, + HasRealPixelSize = 8192, + ReadOnly = 65536, + Caching = 131072, + } + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.ImageFormatConverter))] + public sealed partial class ImageFormat + { + public ImageFormat(System.Guid guid) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.ImageFormat Bmp { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Emf { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Exif { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Gif { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Guid Guid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Icon { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Jpeg { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat MemoryBmp { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Png { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Tiff { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Imaging.ImageFormat Wmf { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? o) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum ImageLockMode + { + ReadOnly = 1, + WriteOnly = 2, + ReadWrite = 3, + UserInputBuffer = 4, + } + [System.ComponentModel.EditorAttribute("System.Drawing.Design.MetafileEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed partial class Metafile : System.Drawing.Image + { + public Metafile(System.IntPtr henhmetafile, bool deleteEmf) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType emfType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType emfType, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr hmetafile, System.Drawing.Imaging.WmfPlaceableFileHeader wmfHeader) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr hmetafile, System.Drawing.Imaging.WmfPlaceableFileHeader wmfHeader, bool deleteWmf) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? desc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(System.IO.Stream stream, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string filename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.Rectangle frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, System.Drawing.Imaging.EmfType type, string? description) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Metafile(string fileName, System.IntPtr referenceHdc, System.Drawing.RectangleF frameRect, System.Drawing.Imaging.MetafileFrameUnit frameUnit, string? desc) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHenhmetafile() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Imaging.MetafileHeader GetMetafileHeader() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.MetafileHeader GetMetafileHeader(System.IntPtr henhmetafile) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.MetafileHeader GetMetafileHeader(System.IntPtr hmetafile, System.Drawing.Imaging.WmfPlaceableFileHeader wmfHeader) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.MetafileHeader GetMetafileHeader(System.IO.Stream stream) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Imaging.MetafileHeader GetMetafileHeader(string fileName) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void PlayRecord(System.Drawing.Imaging.EmfPlusRecordType recordType, int flags, int dataSize, byte[] data) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum MetafileFrameUnit + { + Pixel = 2, + Point = 3, + Inch = 4, + Document = 5, + Millimeter = 6, + GdiCompatible = 7, + } + public sealed partial class MetafileHeader + { + internal MetafileHeader() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Rectangle Bounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float DpiX { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float DpiY { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int EmfPlusHeaderSize { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int LogicalDpiX { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int LogicalDpiY { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int MetafileSize { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.MetafileType Type { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Version { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Imaging.MetaHeader WmfHeader { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsDisplay() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmf() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmfOrEmfPlus() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmfPlus() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmfPlusDual() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsEmfPlusOnly() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsWmf() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsWmfPlaceable() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum MetafileType + { + Invalid = 0, + Wmf = 1, + WmfPlaceable = 2, + Emf = 3, + EmfPlusOnly = 4, + EmfPlusDual = 5, + } + public sealed partial class MetaHeader + { + public MetaHeader() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public short HeaderSize { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int MaxRecord { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short NoObjects { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short NoParameters { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Size { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Type { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Version { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + [System.FlagsAttribute] + public enum PaletteFlags + { + HasAlpha = 1, + GrayScale = 2, + Halftone = 4, + } + public enum PixelFormat + { + DontCare = 0, + Undefined = 0, + Max = 15, + Indexed = 65536, + Gdi = 131072, + Format16bppRgb555 = 135173, + Format16bppRgb565 = 135174, + Format24bppRgb = 137224, + Format32bppRgb = 139273, + Format1bppIndexed = 196865, + Format4bppIndexed = 197634, + Format8bppIndexed = 198659, + Alpha = 262144, + Format16bppArgb1555 = 397319, + PAlpha = 524288, + Format32bppPArgb = 925707, + Extended = 1048576, + Format16bppGrayScale = 1052676, + Format48bppRgb = 1060876, + Format64bppPArgb = 1851406, + Canonical = 2097152, + Format32bppArgb = 2498570, + Format64bppArgb = 3424269, + } + public delegate void PlayRecordCallback(System.Drawing.Imaging.EmfPlusRecordType recordType, int flags, int dataSize, System.IntPtr recordData); + public sealed partial class PropertyItem + { + internal PropertyItem() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Id { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Len { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Type { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public byte[]? Value { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public sealed partial class WmfPlaceableFileHeader + { + public WmfPlaceableFileHeader() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public short BboxBottom { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short BboxLeft { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short BboxRight { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short BboxTop { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Checksum { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Hmf { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Inch { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Key { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Reserved { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } +} +namespace System.Drawing.Printing +{ + public enum Duplex + { + Default = -1, + Simplex = 1, + Vertical = 2, + Horizontal = 3, + } + public partial class InvalidPrinterException : System.SystemException + { + public InvalidPrinterException(System.Drawing.Printing.PrinterSettings settings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected InvalidPrinterException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.ComponentModel.TypeConverterAttribute(typeof(System.Drawing.Printing.MarginsConverter))] + public partial class Margins : System.ICloneable + { + public Margins() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public Margins(int left, int right, int top, int bottom) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Bottom { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Left { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Right { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Top { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override int GetHashCode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool operator ==(System.Drawing.Printing.Margins? m1, System.Drawing.Printing.Margins? m2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static bool operator !=(System.Drawing.Printing.Margins? m1, System.Drawing.Printing.Margins? m2) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class MarginsConverter : System.ComponentModel.ExpandableObjectConverter + { + public MarginsConverter() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Type sourceType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext? context, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] System.Type? destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertFrom(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object? ConvertTo(System.ComponentModel.ITypeDescriptorContext? context, System.Globalization.CultureInfo? culture, object? value, System.Type destinationType) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override object CreateInstance(System.ComponentModel.ITypeDescriptorContext? context, System.Collections.IDictionary propertyValues) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool GetCreateInstanceSupported(System.ComponentModel.ITypeDescriptorContext? context) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PageSettings : System.ICloneable + { + public PageSettings() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PageSettings(System.Drawing.Printing.PrinterSettings printerSettings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Rectangle Bounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool Color { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float HardMarginX { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public float HardMarginY { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool Landscape { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.Margins Margins { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PaperSize PaperSize { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PaperSource PaperSource { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.RectangleF PrintableArea { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrinterResolution PrinterResolution { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrinterSettings PrinterSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyToHdevmode(System.IntPtr hdevmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetHdevmode(System.IntPtr hdevmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum PaperKind + { + Custom = 0, + Letter = 1, + LetterSmall = 2, + Tabloid = 3, + Ledger = 4, + Legal = 5, + Statement = 6, + Executive = 7, + A3 = 8, + A4 = 9, + A4Small = 10, + A5 = 11, + B4 = 12, + B5 = 13, + Folio = 14, + Quarto = 15, + Standard10x14 = 16, + Standard11x17 = 17, + Note = 18, + Number9Envelope = 19, + Number10Envelope = 20, + Number11Envelope = 21, + Number12Envelope = 22, + Number14Envelope = 23, + CSheet = 24, + DSheet = 25, + ESheet = 26, + DLEnvelope = 27, + C5Envelope = 28, + C3Envelope = 29, + C4Envelope = 30, + C6Envelope = 31, + C65Envelope = 32, + B4Envelope = 33, + B5Envelope = 34, + B6Envelope = 35, + ItalyEnvelope = 36, + MonarchEnvelope = 37, + PersonalEnvelope = 38, + USStandardFanfold = 39, + GermanStandardFanfold = 40, + GermanLegalFanfold = 41, + IsoB4 = 42, + JapanesePostcard = 43, + Standard9x11 = 44, + Standard10x11 = 45, + Standard15x11 = 46, + InviteEnvelope = 47, + LetterExtra = 50, + LegalExtra = 51, + TabloidExtra = 52, + A4Extra = 53, + LetterTransverse = 54, + A4Transverse = 55, + LetterExtraTransverse = 56, + APlus = 57, + BPlus = 58, + LetterPlus = 59, + A4Plus = 60, + A5Transverse = 61, + B5Transverse = 62, + A3Extra = 63, + A5Extra = 64, + B5Extra = 65, + A2 = 66, + A3Transverse = 67, + A3ExtraTransverse = 68, + JapaneseDoublePostcard = 69, + A6 = 70, + JapaneseEnvelopeKakuNumber2 = 71, + JapaneseEnvelopeKakuNumber3 = 72, + JapaneseEnvelopeChouNumber3 = 73, + JapaneseEnvelopeChouNumber4 = 74, + LetterRotated = 75, + A3Rotated = 76, + A4Rotated = 77, + A5Rotated = 78, + B4JisRotated = 79, + B5JisRotated = 80, + JapanesePostcardRotated = 81, + JapaneseDoublePostcardRotated = 82, + A6Rotated = 83, + JapaneseEnvelopeKakuNumber2Rotated = 84, + JapaneseEnvelopeKakuNumber3Rotated = 85, + JapaneseEnvelopeChouNumber3Rotated = 86, + JapaneseEnvelopeChouNumber4Rotated = 87, + B6Jis = 88, + B6JisRotated = 89, + Standard12x11 = 90, + JapaneseEnvelopeYouNumber4 = 91, + JapaneseEnvelopeYouNumber4Rotated = 92, + Prc16K = 93, + Prc32K = 94, + Prc32KBig = 95, + PrcEnvelopeNumber1 = 96, + PrcEnvelopeNumber2 = 97, + PrcEnvelopeNumber3 = 98, + PrcEnvelopeNumber4 = 99, + PrcEnvelopeNumber5 = 100, + PrcEnvelopeNumber6 = 101, + PrcEnvelopeNumber7 = 102, + PrcEnvelopeNumber8 = 103, + PrcEnvelopeNumber9 = 104, + PrcEnvelopeNumber10 = 105, + Prc16KRotated = 106, + Prc32KRotated = 107, + Prc32KBigRotated = 108, + PrcEnvelopeNumber1Rotated = 109, + PrcEnvelopeNumber2Rotated = 110, + PrcEnvelopeNumber3Rotated = 111, + PrcEnvelopeNumber4Rotated = 112, + PrcEnvelopeNumber5Rotated = 113, + PrcEnvelopeNumber6Rotated = 114, + PrcEnvelopeNumber7Rotated = 115, + PrcEnvelopeNumber8Rotated = 116, + PrcEnvelopeNumber9Rotated = 117, + PrcEnvelopeNumber10Rotated = 118, + } + public partial class PaperSize + { + public PaperSize() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public PaperSize(string name, int width, int height) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Height { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PaperKind Kind { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string PaperName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int RawKind { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Width { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PaperSource + { + public PaperSource() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Printing.PaperSourceKind Kind { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int RawKind { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string SourceName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum PaperSourceKind + { + Upper = 1, + Lower = 2, + Middle = 3, + Manual = 4, + Envelope = 5, + ManualFeed = 6, + AutomaticFeed = 7, + TractorFeed = 8, + SmallFormat = 9, + LargeFormat = 10, + LargeCapacity = 11, + Cassette = 14, + FormSource = 15, + Custom = 257, + } + public sealed partial class PreviewPageInfo + { + public PreviewPageInfo(System.Drawing.Image image, System.Drawing.Size physicalSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Image Image { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Size PhysicalSize { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public partial class PreviewPrintController : System.Drawing.Printing.PrintController + { + public PreviewPrintController() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override bool IsPreview { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual bool UseAntiAlias { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PreviewPageInfo[] GetPreviewPageInfo() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnEndPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnEndPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override System.Drawing.Graphics OnStartPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnStartPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum PrintAction + { + PrintToFile = 0, + PrintToPreview = 1, + PrintToPrinter = 2, + } + public abstract partial class PrintController + { + protected PrintController() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public virtual bool IsPreview { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual void OnEndPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public virtual void OnEndPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public virtual System.Drawing.Graphics? OnStartPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public virtual void OnStartPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + [System.ComponentModel.DefaultEventAttribute("PrintPage")] + [System.ComponentModel.DefaultPropertyAttribute("DocumentName")] + public partial class PrintDocument : System.ComponentModel.Component + { + public PrintDocument() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + [System.ComponentModel.BrowsableAttribute(false)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public System.Drawing.Printing.PageSettings DefaultPageSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DefaultValueAttribute("document")] + public string DocumentName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.DefaultValueAttribute(false)] + public bool OriginAtMargins { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public System.Drawing.Printing.PrintController PrintController { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.BrowsableAttribute(false)] + [System.ComponentModel.DesignerSerializationVisibilityAttribute(System.ComponentModel.DesignerSerializationVisibility.Hidden)] + public System.Drawing.Printing.PrinterSettings PrinterSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public event System.Drawing.Printing.PrintEventHandler BeginPrint { add { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } remove { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public event System.Drawing.Printing.PrintEventHandler EndPrint { add { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } remove { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public event System.Drawing.Printing.PrintPageEventHandler PrintPage { add { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } remove { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public event System.Drawing.Printing.QueryPageSettingsEventHandler QueryPageSettings { add { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } remove { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + protected internal virtual void OnBeginPrint(System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected internal virtual void OnEndPrint(System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected internal virtual void OnPrintPage(System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected internal virtual void OnQueryPageSettings(System.Drawing.Printing.QueryPageSettingsEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void Print() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PrinterResolution + { + public PrinterResolution() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Printing.PrinterResolutionKind Kind { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int X { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int Y { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public enum PrinterResolutionKind + { + High = -4, + Medium = -3, + Low = -2, + Draft = -1, + Custom = 0, + } + public partial class PrinterSettings : System.ICloneable + { + public PrinterSettings() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool CanDuplex { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool Collate { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public short Copies { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PageSettings DefaultPageSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.Duplex Duplex { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int FromPage { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public static System.Drawing.Printing.PrinterSettings.StringCollection InstalledPrinters { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsDefaultPrinter { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsPlotter { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool IsValid { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int LandscapeAngle { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int MaximumCopies { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int MaximumPage { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int MinimumPage { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrinterSettings.PaperSizeCollection PaperSizes { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrinterSettings.PaperSourceCollection PaperSources { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string PrinterName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrinterSettings.PrinterResolutionCollection PrinterResolutions { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public string PrintFileName { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PrintRange PrintRange { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool PrintToFile { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool SupportsColor { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public int ToPage { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public object Clone() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Graphics CreateMeasurementGraphics() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Graphics CreateMeasurementGraphics(bool honorOriginAtMargins) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Graphics CreateMeasurementGraphics(System.Drawing.Printing.PageSettings pageSettings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Graphics CreateMeasurementGraphics(System.Drawing.Printing.PageSettings pageSettings, bool honorOriginAtMargins) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHdevmode() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHdevmode(System.Drawing.Printing.PageSettings pageSettings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.IntPtr GetHdevnames() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsDirectPrintingSupported(System.Drawing.Image image) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool IsDirectPrintingSupported(System.Drawing.Imaging.ImageFormat imageFormat) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetHdevmode(System.IntPtr hdevmode) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void SetHdevnames(System.IntPtr hdevnames) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override string ToString() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public partial class PaperSizeCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + public PaperSizeCollection(System.Drawing.Printing.PaperSize[] array) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual System.Drawing.Printing.PaperSize this[int index] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + int System.Collections.ICollection.Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + bool System.Collections.ICollection.IsSynchronized { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + object System.Collections.ICollection.SyncRoot { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public int Add(System.Drawing.Printing.PaperSize paperSize) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyTo(System.Drawing.Printing.PaperSize[] paperSizes, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Collections.IEnumerator GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Collections.ICollection.CopyTo(System.Array array, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PaperSourceCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + public PaperSourceCollection(System.Drawing.Printing.PaperSource[] array) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual System.Drawing.Printing.PaperSource this[int index] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + int System.Collections.ICollection.Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + bool System.Collections.ICollection.IsSynchronized { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + object System.Collections.ICollection.SyncRoot { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public int Add(System.Drawing.Printing.PaperSource paperSource) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyTo(System.Drawing.Printing.PaperSource[] paperSources, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Collections.IEnumerator GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Collections.ICollection.CopyTo(System.Array array, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PrinterResolutionCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + public PrinterResolutionCollection(System.Drawing.Printing.PrinterResolution[] array) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual System.Drawing.Printing.PrinterResolution this[int index] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + int System.Collections.ICollection.Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + bool System.Collections.ICollection.IsSynchronized { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + object System.Collections.ICollection.SyncRoot { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public int Add(System.Drawing.Printing.PrinterResolution printerResolution) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyTo(System.Drawing.Printing.PrinterResolution[] printerResolutions, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Collections.IEnumerator GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Collections.ICollection.CopyTo(System.Array array, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class StringCollection : System.Collections.ICollection, System.Collections.IEnumerable + { + public StringCollection(string[] array) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public int Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public virtual string this[int index] { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + int System.Collections.ICollection.Count { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + bool System.Collections.ICollection.IsSynchronized { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + object System.Collections.ICollection.SyncRoot { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] + public int Add(string value) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void CopyTo(string[] strings, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Collections.IEnumerator GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + void System.Collections.ICollection.CopyTo(System.Array array, int index) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + } + public enum PrinterUnit + { + Display = 0, + ThousandthsOfAnInch = 1, + HundredthsOfAMillimeter = 2, + TenthsOfAMillimeter = 3, + } + public sealed partial class PrinterUnitConvert + { + internal PrinterUnitConvert() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static double Convert(double value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Point Convert(System.Drawing.Point value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Printing.Margins Convert(System.Drawing.Printing.Margins value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Rectangle Convert(System.Drawing.Rectangle value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static System.Drawing.Size Convert(System.Drawing.Size value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public static int Convert(int value, System.Drawing.Printing.PrinterUnit fromUnit, System.Drawing.Printing.PrinterUnit toUnit) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public partial class PrintEventArgs : System.ComponentModel.CancelEventArgs + { + public PrintEventArgs() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Printing.PrintAction PrintAction { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public delegate void PrintEventHandler(object sender, System.Drawing.Printing.PrintEventArgs e); + public partial class PrintPageEventArgs : System.EventArgs + { + public PrintPageEventArgs(System.Drawing.Graphics? graphics, System.Drawing.Rectangle marginBounds, System.Drawing.Rectangle pageBounds, System.Drawing.Printing.PageSettings pageSettings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public bool Cancel { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Graphics? Graphics { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public bool HasMorePages { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Rectangle MarginBounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Rectangle PageBounds { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public System.Drawing.Printing.PageSettings PageSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public delegate void PrintPageEventHandler(object sender, System.Drawing.Printing.PrintPageEventArgs e); + public enum PrintRange + { + AllPages = 0, + Selection = 1, + SomePages = 2, + CurrentPage = 4194304, + } + public partial class QueryPageSettingsEventArgs : System.Drawing.Printing.PrintEventArgs + { + public QueryPageSettingsEventArgs(System.Drawing.Printing.PageSettings pageSettings) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.Printing.PageSettings PageSettings { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } set { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + } + public delegate void QueryPageSettingsEventHandler(object sender, System.Drawing.Printing.QueryPageSettingsEventArgs e); + public partial class StandardPrintController : System.Drawing.Printing.PrintController + { + public StandardPrintController() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnEndPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnEndPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override System.Drawing.Graphics OnStartPage(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintPageEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public override void OnStartPrint(System.Drawing.Printing.PrintDocument document, System.Drawing.Printing.PrintEventArgs e) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } +} +namespace System.Drawing.Text +{ + public abstract partial class FontCollection : System.IDisposable + { + internal FontCollection() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public System.Drawing.FontFamily[] Families { get { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } } + public void Dispose() { } + protected virtual void Dispose(bool disposing) { } + ~FontCollection() { } + } + public enum GenericFontFamilies + { + Serif = 0, + SansSerif = 1, + Monospace = 2, + } + public enum HotkeyPrefix + { + None = 0, + Show = 1, + Hide = 2, + } + public sealed partial class InstalledFontCollection : System.Drawing.Text.FontCollection + { + public InstalledFontCollection() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + } + public sealed partial class PrivateFontCollection : System.Drawing.Text.FontCollection + { + public PrivateFontCollection() { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddFontFile(string filename) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + public void AddMemoryFont(System.IntPtr memory, int length) { throw new System.PlatformNotSupportedException(System.SR.SystemDrawingCommon_PlatformNotSupported); } + protected override void Dispose(bool disposing) { } + } + public enum TextRenderingHint + { + SystemDefault = 0, + SingleBitPerPixelGridFit = 1, + SingleBitPerPixel = 2, + AntiAliasGridFit = 3, + AntiAlias = 4, + ClearTypeGridFit = 5, + } +} + +#pragma warning restore CS8618 diff --git a/src/System.Drawing.Common/src/Resources/Strings.resx b/src/System.Drawing.Common/src/Resources/Strings.resx new file mode 100644 index 00000000000..eb89ee9ebb7 --- /dev/null +++ b/src/System.Drawing.Common/src/Resources/Strings.resx @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + (printer name protected due to security restrictions) + + + Changes cannot be made to {0} because permissions are not valid. + + + Bitmaps that are icons cannot be made transparent. Icons natively support transparency. Use the Icon constructor to create an icon. + + + The color {0} is not a system color. + + + Function was ended. + + + File access is denied. + + + A Graphics object cannot be created from an image that has an indexed pixel format. + + + SetPixel is not supported for images with indexed pixel formats. + + + Destination points define a parallelogram which must have a length of 3. These points will represent the upper-left, upper-right, and lower-left coordinates (defined in that order). + + + Destination points must be an array with a length of 3 or 4. A length of 3 defines a parallelogram with the upper-left, upper-right, and lower-left corners. A length of 4 defines a quadrilateral with the fourth element of the array specifying the lower-right coordinate. + + + File not found. + + + Font '{0}' cannot be found. + + + Font '{0}' does not support style '{1}'. + + + A generic error occurred in GDI+. + + + Buffer is too small (internal GDI+ error). + + + Parameter is not valid. + + + Rectangle '{0}' cannot have a width or height equal to 0. + + + Operation requires a transformation of the image from GDI+ to GDI. GDI does not support images with a width or height greater than 32767. + + + Out of memory. + + + Not implemented. + + + GDI+ is not properly initialized (internal GDI+ error). + + + Only TrueType fonts are supported. '{0}' is not a TrueType font. + + + Only TrueType fonts are supported. This is not a TrueType font. + + + Object is currently in use elsewhere. + + + Overflow error. + + + Property cannot be found. + + + Property is not supported. + + + Unknown GDI+ error occurred. + + + Image format is unknown. + + + Current version of GDI+ does not support this feature. + + + Bitmap region is already locked. + + + (Global Assembly Cache) + + + BufferedGraphicsContext cannot be disposed of because a buffer operation is currently in progress. + + + Screen-compatible bitmap cannot be created. The screen bitmap format cannot be determined. + + + '{0}' data length expected {1}, read {2} + + + Internal state of the {0} class is invalid. + + + Property must be set to a valid ColorBlend object to use interpolation colors. + + + {0}{1} ColorBlend objects must be constructed with the same number of positions and color values. Positions must be between 0.0 and 1.0, 1.0 indicating the last element in the array. + + + ColorBlend object that was set is not valid. + + + Position's first element must be equal to 0. + + + Position's last element must be equal to 1.0. + + + Array of colors and positions must contain at least two elements. + + + Colors and positions do not have the same number of elements. + + + Value of '{1}' is not valid for '{0}'. + + + Value of '{0}' is not valid for font size unit. + + + Value of '{1}' is not valid for '{0}'. '{0}' should be greater than {2} and less than or equal to {3}. + + + Color '{0}' is not valid. + + + DashPattern value is not valid. + + + Value of '{1}' is not valid for '{0}'. '{0}' should be greater than or equal to {2} and less than or equal to {3}. + + + Win32 handle that was passed to {0} is not valid or is the wrong type. + + + Image type is unknown. + + + Value of '{1}' is not valid for '{0}'. '{0}' must be greater than or equal to {2}. + + + Permission state is not valid. + + + Argument '{0}' must be a picture that can be used as a {1}. + + + Settings to access printer '{0}' are not valid. + + + No printers are installed. + + + Handle {0} is not valid. + + + Parameter must be positive and < Width. + + + Parameter must be positive and < Height. + + + Native handle is 0. + + + Default printer is not set. + + + Not implemented. + + + Occurs when the document is about to be printed. + + + The name of the document shown to the user. + + + The page settings of the page currently being printed. + + + Occurs after the document has been printed. + + + Indicates that the graphics origin is located at the user-specified page margins. + + + Retrieves the print controller for this document. + + + Occurs once for each page to be printed. + + + Retrieves the settings for the printer the document is currently being printed to. + + + Occurs before each page is printed. Useful for changing PageSettings for a particular page. + + + System.Drawing.Common is not supported on non-Windows platforms. See https://aka.ms/systemdrawingnonwindows for more information. + + + Defines an object that sends output to a printer. + + + IDictionary parameter contains at least one entry that is not valid. Ensure all values are consistent with the object's properties. + + + PaperSize cannot be changed unless the Kind property is set to Custom. + + + Resource '{1}' cannot be found in class '{0}'. + + + Text "{0}" cannot be parsed. The expected text format is "{1}". + + + TriState.Default cannot be converted into a Boolean. + + + (Icon) + + + (none) + + + GetObjectType on this dc returned an invalid value. + + + The value of argument '{0}' ({1}) is invalid for Enum type '{2}'. + + + {0} is not a valid value for {1}. + + + Invalid Blend object. It should have at least 2 elements in each of the factors and positions arrays. + + + Invalid Blend object. The positions array must have 0.0 as its first element. + + + Invalid Blend object. The positions array must have 1.0 as its last element. + + + {0} only available on WMF files. + + + Cannot create Graphics from an indexed bitmap. + + + Could not open display (X-Server required. Check your DISPLAY environment variable) + + + Couldn't find specified file. + + + Icon instance was disposed. + + + Invalid GraphicsUnit + + + Invalid thumbnail size + + + No codec available for format:{0} + + + Operation not implemented under X11 + + + (none) + + + No valid icon image found + + + Null or empty path. + + + Invalid parameter passed. Number of points and types must be same. + + + Object has been disposed. + + + The value of the {0} property is less than zero. + + + The value of the {0} property is not one of the {1} values + + + The directory {0} of the filename {1} does not exist. + + + System.Drawing.Common is not supported on this platform. + + diff --git a/src/System.Drawing.Common/src/Resources/System/Drawing/DefaultComponent.bmp b/src/System.Drawing.Common/src/Resources/System/Drawing/DefaultComponent.bmp new file mode 100644 index 00000000000..e7a9eb9331a Binary files /dev/null and b/src/System.Drawing.Common/src/Resources/System/Drawing/DefaultComponent.bmp differ diff --git a/src/System.Drawing.Common/src/Resources/System/Drawing/Error.ico b/src/System.Drawing.Common/src/Resources/System/Drawing/Error.ico new file mode 100644 index 00000000000..8648d511f49 Binary files /dev/null and b/src/System.Drawing.Common/src/Resources/System/Drawing/Error.ico differ diff --git a/src/System.Drawing.Common/src/Resources/System/Drawing/Printing/PrintDocument.bmp b/src/System.Drawing.Common/src/Resources/System/Drawing/Printing/PrintDocument.bmp new file mode 100644 index 00000000000..9473fc5a52b Binary files /dev/null and b/src/System.Drawing.Common/src/Resources/System/Drawing/Printing/PrintDocument.bmp differ diff --git a/src/System.Drawing.Common/src/Resources/System/Drawing/ShieldIcon.ico b/src/System.Drawing.Common/src/Resources/System/Drawing/ShieldIcon.ico new file mode 100644 index 00000000000..0bc48439a11 Binary files /dev/null and b/src/System.Drawing.Common/src/Resources/System/Drawing/ShieldIcon.ico differ diff --git a/src/System.Drawing.Common/src/SRDescriptionAttribute.cs b/src/System.Drawing.Common/src/SRDescriptionAttribute.cs new file mode 100644 index 00000000000..6e81b29eba1 --- /dev/null +++ b/src/System.Drawing.Common/src/SRDescriptionAttribute.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace System.Drawing +{ + [AttributeUsage(AttributeTargets.All)] + internal sealed class SRDescriptionAttribute : DescriptionAttribute + { + private bool _replaced; + + public override string Description + { + get + { + if (!_replaced) + { + _replaced = true; + DescriptionValue = SR.Format(base.Description); + } + return base.Description; + } + } + + public SRDescriptionAttribute(string description) : base(description) + { + } + } +} diff --git a/src/System.Drawing.Common/src/System.Drawing.Common.csproj b/src/System.Drawing.Common/src/System.Drawing.Common.csproj new file mode 100644 index 00000000000..6945f731a84 --- /dev/null +++ b/src/System.Drawing.Common/src/System.Drawing.Common.csproj @@ -0,0 +1,343 @@ + + + + net8.0;net7.0;net6.0;netstandard2.0;net462 + + true + CS0618 + false + enable + disable + true + true + true + true + 7.0.0 + true + Provides access to GDI+ graphics functionality. + +Commonly Used Types: +System.Drawing.Bitmap +System.Drawing.BitmapData +System.Drawing.Brush +System.Drawing.Font +System.Drawing.Graphics +System.Drawing.Icon + +Since .NET 7, non-Windows platforms are not supported, even with the runtime configuration switch. See https://aka.ms/systemdrawingnonwindows for more information. + + + + + true + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs b/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs new file mode 100644 index 00000000000..85a5a801219 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Bitmap.cs @@ -0,0 +1,369 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + [Editor("System.Drawing.Design.BitmapEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed class Bitmap : Image + { + private static readonly Color s_defaultTransparentColor = Color.LightGray; + + private Bitmap() { } + + internal Bitmap(IntPtr ptr) => SetNativeImage(ptr); + + public Bitmap(string filename) : this(filename, useIcm: false) { } + + public Bitmap(string filename, bool useIcm) + { + // GDI+ will read this file multiple times. Get the fully qualified path + // so if the app's default directory changes we won't get an error. + filename = Path.GetFullPath(filename); + + IntPtr bitmap; + int status; + + if (useIcm) + { + status = Gdip.GdipCreateBitmapFromFileICM(filename, out bitmap); + } + else + { + status = Gdip.GdipCreateBitmapFromFile(filename, out bitmap); + } + Gdip.CheckStatus(status); + + ValidateImage(bitmap); + + SetNativeImage(bitmap); + EnsureSave(this, filename, null); + } + + public Bitmap(Stream stream) : this(stream, false) + { + } + + public unsafe Bitmap(Stream stream, bool useIcm) + { + ArgumentNullException.ThrowIfNull(stream); + + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr bitmap = IntPtr.Zero; + if (useIcm) + { + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStreamICM(streamWrapper.Ptr, &bitmap)); + } + else + { + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromStream(streamWrapper.Ptr, &bitmap)); + } + + ValidateImage(bitmap); + + SetNativeImage(bitmap); + EnsureSave(this, null, stream); + } + + public Bitmap(Type type, string resource) : this(GetResourceStream(type, resource)) + { + } + + private static Stream GetResourceStream(Type type, string resource) + { + ArgumentNullException.ThrowIfNull(type); + ArgumentNullException.ThrowIfNull(resource); + + Stream? stream = type.Module.Assembly.GetManifestResourceStream(type, resource); + if (stream == null) + { + throw new ArgumentException(SR.Format(SR.ResourceNotFound, type, resource)); + } + + return stream; + } + + public Bitmap(int width, int height) : this(width, height, PixelFormat.Format32bppArgb) + { + } + + public Bitmap(int width, int height, Graphics g) + { + ArgumentNullException.ThrowIfNull(g); + + IntPtr bitmap; + int status = Gdip.GdipCreateBitmapFromGraphics(width, height, new HandleRef(g, g.NativeGraphics), out bitmap); + Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(int width, int height, int stride, PixelFormat format, IntPtr scan0) + { + IntPtr bitmap; + int status = Gdip.GdipCreateBitmapFromScan0(width, height, stride, unchecked((int)format), scan0, out bitmap); + Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(int width, int height, PixelFormat format) + { + IntPtr bitmap; + int status = Gdip.GdipCreateBitmapFromScan0(width, height, 0, unchecked((int)format), IntPtr.Zero, out bitmap); + Gdip.CheckStatus(status); + + SetNativeImage(bitmap); + } + + public Bitmap(Image original) : this(original, original.Width, original.Height) + { + } + + public Bitmap(Image original, Size newSize) : this(original, newSize.Width, newSize.Height) + { + } + + public Bitmap(Image original, int width, int height) : this(width, height, PixelFormat.Format32bppArgb) + { + ArgumentNullException.ThrowIfNull(original); + + using (Graphics g = Graphics.FromImage(this)) + { + g.Clear(Color.Transparent); + g.DrawImage(original, 0, 0, width, height); + } + } + + private Bitmap(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + public static Bitmap FromHicon(IntPtr hicon) + { + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromHICON(hicon, out IntPtr bitmap)); + return new Bitmap(bitmap); + } + + public static Bitmap FromResource(IntPtr hinstance, string bitmapName) + { + IntPtr name = Marshal.StringToHGlobalUni(bitmapName); + try + { + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromResource(hinstance, name, out IntPtr bitmap)); + return new Bitmap(bitmap); + } + finally + { + Marshal.FreeHGlobal(name); + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHbitmap() => GetHbitmap(Color.LightGray); + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHbitmap(Color background) + { + IntPtr hBitmap; + int status = Gdip.GdipCreateHBITMAPFromBitmap(new HandleRef(this, nativeImage), out hBitmap, + ColorTranslator.ToWin32(background)); + if (status == 2 /* invalid parameter*/ && (Width >= short.MaxValue || Height >= short.MaxValue)) + { + throw new ArgumentException(SR.GdiplusInvalidSize); + } + + Gdip.CheckStatus(status); + + return hBitmap; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public IntPtr GetHicon() + { + IntPtr hIcon; + int status = Gdip.GdipCreateHICONFromBitmap(new HandleRef(this, nativeImage), out hIcon); + Gdip.CheckStatus(status); + + return hIcon; + } + + public Bitmap Clone(RectangleF rect, PixelFormat format) + { + if (rect.Width == 0 || rect.Height == 0) + { + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + } + + IntPtr dstHandle; + + int status = Gdip.GdipCloneBitmapArea( + rect.X, + rect.Y, + rect.Width, + rect.Height, + unchecked((int)format), + new HandleRef(this, nativeImage), + out dstHandle); + + if (status != Gdip.Ok || dstHandle == IntPtr.Zero) + throw Gdip.StatusException(status); + + return new Bitmap(dstHandle); + } + + public void MakeTransparent() + { + Color transparent = s_defaultTransparentColor; + if (Height > 0 && Width > 0) + { + transparent = GetPixel(0, Size.Height - 1); + } + if (transparent.A < 255) + { + // It's already transparent, and if we proceeded, we will do something + // unintended like making black transparent + return; + } + + MakeTransparent(transparent); + } + + public void MakeTransparent(Color transparentColor) + { + if (RawFormat.Guid == ImageFormat.Icon.Guid) + { + throw new InvalidOperationException(SR.CantMakeIconTransparent); + } + + Size size = Size; + + // The new bitmap must be in 32bppARGB format, because that's the only + // thing that supports alpha. (And that's what the image is initialized to -- transparent) + using (var result = new Bitmap(size.Width, size.Height, PixelFormat.Format32bppArgb)) + using (Graphics graphics = Graphics.FromImage(result)) + { + graphics.Clear(Color.Transparent); + Rectangle rectangle = new Rectangle(0, 0, size.Width, size.Height); + + using (var attributes = new ImageAttributes()) + { + attributes.SetColorKey(transparentColor, transparentColor); + graphics.DrawImage(this, rectangle, + 0, 0, size.Width, size.Height, + GraphicsUnit.Pixel, attributes, null, IntPtr.Zero); + } + + // Swap nativeImage pointers to make it look like we modified the image in place + IntPtr temp = nativeImage; + nativeImage = result.nativeImage; + result.nativeImage = temp; + } + } + + public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format) + { + return LockBits(rect, flags, format, new BitmapData()); + } + + public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format, BitmapData bitmapData) + { + int status = Gdip.GdipBitmapLockBits( + new HandleRef(this, nativeImage), ref rect, flags, format, bitmapData); + + // libgdiplus has the wrong error code mapping for this state. + if (status == 7) + { + status = 8; + } + Gdip.CheckStatus(status); + + return bitmapData; + } + + public void UnlockBits(BitmapData bitmapdata) + { + int status = Gdip.GdipBitmapUnlockBits(new HandleRef(this, nativeImage), bitmapdata); + Gdip.CheckStatus(status); + } + + public Color GetPixel(int x, int y) + { + if (x < 0 || x >= Width) + { + throw new ArgumentOutOfRangeException(nameof(x), SR.ValidRangeX); + } + + if (y < 0 || y >= Height) + { + throw new ArgumentOutOfRangeException(nameof(y), SR.ValidRangeY); + } + + int color; + int status = Gdip.GdipBitmapGetPixel(new HandleRef(this, nativeImage), x, y, out color); + Gdip.CheckStatus(status); + + return Color.FromArgb(color); + } + + public void SetPixel(int x, int y, Color color) + { + if ((PixelFormat & PixelFormat.Indexed) != 0) + { + throw new InvalidOperationException(SR.GdiplusCannotSetPixelFromIndexedPixelFormat); + } + + if (x < 0 || x >= Width) + { + throw new ArgumentOutOfRangeException(nameof(x), SR.ValidRangeX); + } + + if (y < 0 || y >= Height) + { + throw new ArgumentOutOfRangeException(nameof(y), SR.ValidRangeY); + } + + int status = Gdip.GdipBitmapSetPixel(new HandleRef(this, nativeImage), x, y, color.ToArgb()); + Gdip.CheckStatus(status); + } + + public void SetResolution(float xDpi, float yDpi) + { + int status = Gdip.GdipBitmapSetResolution(new HandleRef(this, nativeImage), xDpi, yDpi); + Gdip.CheckStatus(status); + } + public Bitmap Clone(Rectangle rect, PixelFormat format) + { + if (rect.Width == 0 || rect.Height == 0) + { + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + } + + IntPtr dstHandle; + int status = Gdip.GdipCloneBitmapAreaI( + rect.X, + rect.Y, + rect.Width, + rect.Height, + unchecked((int)format), + new HandleRef(this, nativeImage), + out dstHandle); + + if (status != Gdip.Ok || dstHandle == IntPtr.Zero) + throw Gdip.StatusException(status); + + return new Bitmap(dstHandle); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs b/src/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs new file mode 100644 index 00000000000..b5daa033aa8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BitmapSelector.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Reflection; + +namespace System.Drawing +{ + /// + /// Provides methods to select bitmaps. + /// + internal static class BitmapSelector + { + /// + /// Returns a resource stream loaded from the appropriate location according to the current + /// suffix. + /// + /// The assembly from which the stream is loaded + /// The type whose namespace is used to scope the manifest resource name + /// The name of the manifest resource being requested + /// + /// The manifest resource stream corresponding to . + /// + public static Stream? GetResourceStream(Assembly assembly, Type type, string originalName) + { + return assembly.GetManifestResourceStream(type, originalName); + } + + /// + /// Returns a resource stream loaded from the appropriate location according to the current + /// suffix. + /// + /// The type from whose assembly the stream is loaded and whose namespace is used to scope the resource name + /// The name of the manifest resource being requested + /// + /// The manifest resource stream corresponding to . + /// + public static Stream? GetResourceStream(Type type, string originalName) + { + return GetResourceStream(type.Module.Assembly, type, originalName); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSameAssemblyAttribute.cs b/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSameAssemblyAttribute.cs new file mode 100644 index 00000000000..ea2a306ed7a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSameAssemblyAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Opt-In flag to look for resources in the same assembly but with the "bitmapSuffix" config setting. + /// i.e. System.Web.UI.WebControl.Button.bmp -> System.Web.UI.WebControl.Button.VisualStudio.11.0.bmp + /// + [AttributeUsage(AttributeTargets.Assembly)] + public class BitmapSuffixInSameAssemblyAttribute : Attribute + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSatelliteAssemblyAttribute.cs b/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSatelliteAssemblyAttribute.cs new file mode 100644 index 00000000000..8e72f54fbd8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BitmapSuffixInSatelliteAssemblyAttribute.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Opt-In flag to look for resources in the another assembly with the "bitmapSuffix" config setting + /// i.e. System.Web.dll -> System.Web.VisualStudio.11.0.dll + /// + [AttributeUsage(AttributeTargets.Assembly)] + public class BitmapSuffixInSatelliteAssemblyAttribute : Attribute + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Brush.cs b/src/System.Drawing.Common/src/System/Drawing/Brush.cs new file mode 100644 index 00000000000..de964a2b13d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Brush.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public abstract class Brush : MarshalByRefObject, ICloneable, IDisposable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + // Handle to native GDI+ brush object to be used on demand. + private IntPtr _nativeBrush; + + public abstract object Clone(); + + protected internal void SetNativeBrush(IntPtr brush) => SetNativeBrushInternal(brush); + internal void SetNativeBrushInternal(IntPtr brush) => _nativeBrush = brush; + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + internal IntPtr NativeBrush => _nativeBrush; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { +#if FINALIZATION_WATCH + if (!disposing && nativeBrush != IntPtr.Zero ) + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); +#endif + + if (_nativeBrush != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteBrush(new HandleRef(this, _nativeBrush)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + // Catch all non fatal exceptions. This includes exceptions like EntryPointNotFoundException, that is thrown + // on Windows Nano. + } + finally + { + _nativeBrush = IntPtr.Zero; + } + } + } + + ~Brush() => Dispose(false); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Brushes.cs b/src/System.Drawing.Common/src/System/Drawing/Brushes.cs new file mode 100644 index 00000000000..f3de18a41c0 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Brushes.cs @@ -0,0 +1,324 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public static class Brushes + { + private static readonly object s_transparentKey = new object(); + private static readonly object s_aliceBlueKey = new object(); + private static readonly object s_antiqueWhiteKey = new object(); + private static readonly object s_aquaKey = new object(); + private static readonly object s_aquamarineKey = new object(); + private static readonly object s_azureKey = new object(); + private static readonly object s_beigeKey = new object(); + private static readonly object s_bisqueKey = new object(); + private static readonly object s_blackKey = new object(); + private static readonly object s_blanchedAlmondKey = new object(); + private static readonly object s_blueKey = new object(); + private static readonly object s_blueVioletKey = new object(); + private static readonly object s_brownKey = new object(); + private static readonly object s_burlyWoodKey = new object(); + private static readonly object s_cadetBlueKey = new object(); + private static readonly object s_chartreuseKey = new object(); + private static readonly object s_chocolateKey = new object(); + private static readonly object s_coralKey = new object(); + private static readonly object s_cornflowerBlueKey = new object(); + private static readonly object s_cornsilkKey = new object(); + private static readonly object s_crimsonKey = new object(); + private static readonly object s_cyanKey = new object(); + private static readonly object s_darkBlueKey = new object(); + private static readonly object s_darkCyanKey = new object(); + private static readonly object s_darkGoldenrodKey = new object(); + private static readonly object s_darkGrayKey = new object(); + private static readonly object s_darkGreenKey = new object(); + private static readonly object s_darkKhakiKey = new object(); + private static readonly object s_darkMagentaKey = new object(); + private static readonly object s_darkOliveGreenKey = new object(); + private static readonly object s_darkOrangeKey = new object(); + private static readonly object s_darkOrchidKey = new object(); + private static readonly object s_darkRedKey = new object(); + private static readonly object s_darkSalmonKey = new object(); + private static readonly object s_darkSeaGreenKey = new object(); + private static readonly object s_darkSlateBlueKey = new object(); + private static readonly object s_darkSlateGrayKey = new object(); + private static readonly object s_darkTurquoiseKey = new object(); + private static readonly object s_darkVioletKey = new object(); + private static readonly object s_deepPinkKey = new object(); + private static readonly object s_deepSkyBlueKey = new object(); + private static readonly object s_dimGrayKey = new object(); + private static readonly object s_dodgerBlueKey = new object(); + private static readonly object s_firebrickKey = new object(); + private static readonly object s_floralWhiteKey = new object(); + private static readonly object s_forestGreenKey = new object(); + private static readonly object s_fuchsiaKey = new object(); + private static readonly object s_gainsboroKey = new object(); + private static readonly object s_ghostWhiteKey = new object(); + private static readonly object s_goldKey = new object(); + private static readonly object s_goldenrodKey = new object(); + private static readonly object s_grayKey = new object(); + private static readonly object s_greenKey = new object(); + private static readonly object s_greenYellowKey = new object(); + private static readonly object s_honeydewKey = new object(); + private static readonly object s_hotPinkKey = new object(); + private static readonly object s_indianRedKey = new object(); + private static readonly object s_indigoKey = new object(); + private static readonly object s_ivoryKey = new object(); + private static readonly object s_khakiKey = new object(); + private static readonly object s_lavenderKey = new object(); + private static readonly object s_lavenderBlushKey = new object(); + private static readonly object s_lawnGreenKey = new object(); + private static readonly object s_lemonChiffonKey = new object(); + private static readonly object s_lightBlueKey = new object(); + private static readonly object s_lightCoralKey = new object(); + private static readonly object s_lightCyanKey = new object(); + private static readonly object s_lightGoldenrodYellowKey = new object(); + private static readonly object s_lightGreenKey = new object(); + private static readonly object s_lightGrayKey = new object(); + private static readonly object s_lightPinkKey = new object(); + private static readonly object s_lightSalmonKey = new object(); + private static readonly object s_lightSeaGreenKey = new object(); + private static readonly object s_lightSkyBlueKey = new object(); + private static readonly object s_lightSlateGrayKey = new object(); + private static readonly object s_lightSteelBlueKey = new object(); + private static readonly object s_lightYellowKey = new object(); + private static readonly object s_limeKey = new object(); + private static readonly object s_limeGreenKey = new object(); + private static readonly object s_linenKey = new object(); + private static readonly object s_magentaKey = new object(); + private static readonly object s_maroonKey = new object(); + private static readonly object s_mediumAquamarineKey = new object(); + private static readonly object s_mediumBlueKey = new object(); + private static readonly object s_mediumOrchidKey = new object(); + private static readonly object s_mediumPurpleKey = new object(); + private static readonly object s_mediumSeaGreenKey = new object(); + private static readonly object s_mediumSlateBlueKey = new object(); + private static readonly object s_mediumSpringGreenKey = new object(); + private static readonly object s_mediumTurquoiseKey = new object(); + private static readonly object s_mediumVioletRedKey = new object(); + private static readonly object s_midnightBlueKey = new object(); + private static readonly object s_mintCreamKey = new object(); + private static readonly object s_mistyRoseKey = new object(); + private static readonly object s_moccasinKey = new object(); + private static readonly object s_navajoWhiteKey = new object(); + private static readonly object s_navyKey = new object(); + private static readonly object s_oldLaceKey = new object(); + private static readonly object s_oliveKey = new object(); + private static readonly object s_oliveDrabKey = new object(); + private static readonly object s_orangeKey = new object(); + private static readonly object s_orangeRedKey = new object(); + private static readonly object s_orchidKey = new object(); + private static readonly object s_paleGoldenrodKey = new object(); + private static readonly object s_paleGreenKey = new object(); + private static readonly object s_paleTurquoiseKey = new object(); + private static readonly object s_paleVioletRedKey = new object(); + private static readonly object s_papayaWhipKey = new object(); + private static readonly object s_peachPuffKey = new object(); + private static readonly object s_peruKey = new object(); + private static readonly object s_pinkKey = new object(); + private static readonly object s_plumKey = new object(); + private static readonly object s_powderBlueKey = new object(); + private static readonly object s_purpleKey = new object(); + private static readonly object s_redKey = new object(); + private static readonly object s_rosyBrownKey = new object(); + private static readonly object s_royalBlueKey = new object(); + private static readonly object s_saddleBrownKey = new object(); + private static readonly object s_salmonKey = new object(); + private static readonly object s_sandyBrownKey = new object(); + private static readonly object s_seaGreenKey = new object(); + private static readonly object s_seaShellKey = new object(); + private static readonly object s_siennaKey = new object(); + private static readonly object s_silverKey = new object(); + private static readonly object s_skyBlueKey = new object(); + private static readonly object s_slateBlueKey = new object(); + private static readonly object s_slateGrayKey = new object(); + private static readonly object s_snowKey = new object(); + private static readonly object s_springGreenKey = new object(); + private static readonly object s_steelBlueKey = new object(); + private static readonly object s_tanKey = new object(); + private static readonly object s_tealKey = new object(); + private static readonly object s_thistleKey = new object(); + private static readonly object s_tomatoKey = new object(); + private static readonly object s_turquoiseKey = new object(); + private static readonly object s_violetKey = new object(); + private static readonly object s_wheatKey = new object(); + private static readonly object s_whiteKey = new object(); + private static readonly object s_whiteSmokeKey = new object(); + private static readonly object s_yellowKey = new object(); + private static readonly object s_yellowGreenKey = new object(); + + public static Brush Transparent => GetBrush(s_transparentKey, Color.Transparent); + + public static Brush AliceBlue => GetBrush(s_aliceBlueKey, Color.AliceBlue); + public static Brush AntiqueWhite => GetBrush(s_antiqueWhiteKey, Color.AntiqueWhite); + public static Brush Aqua => GetBrush(s_aquaKey, Color.Aqua); + public static Brush Aquamarine => GetBrush(s_aquamarineKey, Color.Aquamarine); + public static Brush Azure => GetBrush(s_azureKey, Color.Azure); + + public static Brush Beige => GetBrush(s_beigeKey, Color.Beige); + public static Brush Bisque => GetBrush(s_bisqueKey, Color.Bisque); + public static Brush Black => GetBrush(s_blackKey, Color.Black); + public static Brush BlanchedAlmond => GetBrush(s_blanchedAlmondKey, Color.BlanchedAlmond); + public static Brush Blue => GetBrush(s_blueKey, Color.Blue); + public static Brush BlueViolet => GetBrush(s_blueVioletKey, Color.BlueViolet); + public static Brush Brown => GetBrush(s_brownKey, Color.Brown); + public static Brush BurlyWood => GetBrush(s_burlyWoodKey, Color.BurlyWood); + + public static Brush CadetBlue => GetBrush(s_cadetBlueKey, Color.CadetBlue); + public static Brush Chartreuse => GetBrush(s_chartreuseKey, Color.Chartreuse); + public static Brush Chocolate => GetBrush(s_chocolateKey, Color.Chocolate); + public static Brush Coral => GetBrush(s_coralKey, Color.Coral); + public static Brush CornflowerBlue => GetBrush(s_cornflowerBlueKey, Color.CornflowerBlue); + public static Brush Cornsilk => GetBrush(s_cornsilkKey, Color.Cornsilk); + public static Brush Crimson => GetBrush(s_crimsonKey, Color.Crimson); + public static Brush Cyan => GetBrush(s_cyanKey, Color.Cyan); + + public static Brush DarkBlue => GetBrush(s_darkBlueKey, Color.DarkBlue); + public static Brush DarkCyan => GetBrush(s_darkCyanKey, Color.DarkCyan); + public static Brush DarkGoldenrod => GetBrush(s_darkGoldenrodKey, Color.DarkGoldenrod); + public static Brush DarkGray => GetBrush(s_darkGrayKey, Color.DarkGray); + public static Brush DarkGreen => GetBrush(s_darkGreenKey, Color.DarkGreen); + public static Brush DarkKhaki => GetBrush(s_darkKhakiKey, Color.DarkKhaki); + public static Brush DarkMagenta => GetBrush(s_darkMagentaKey, Color.DarkMagenta); + public static Brush DarkOliveGreen => GetBrush(s_darkOliveGreenKey, Color.DarkOliveGreen); + public static Brush DarkOrange => GetBrush(s_darkOrangeKey, Color.DarkOrange); + public static Brush DarkOrchid => GetBrush(s_darkOrchidKey, Color.DarkOrchid); + public static Brush DarkRed => GetBrush(s_darkRedKey, Color.DarkRed); + public static Brush DarkSalmon => GetBrush(s_darkSalmonKey, Color.DarkSalmon); + public static Brush DarkSeaGreen => GetBrush(s_darkSeaGreenKey, Color.DarkSeaGreen); + public static Brush DarkSlateBlue => GetBrush(s_darkSlateBlueKey, Color.DarkSlateBlue); + public static Brush DarkSlateGray => GetBrush(s_darkSlateGrayKey, Color.DarkSlateGray); + public static Brush DarkTurquoise => GetBrush(s_darkTurquoiseKey, Color.DarkTurquoise); + public static Brush DarkViolet => GetBrush(s_darkVioletKey, Color.DarkViolet); + public static Brush DeepPink => GetBrush(s_deepPinkKey, Color.DeepPink); + public static Brush DeepSkyBlue => GetBrush(s_deepSkyBlueKey, Color.DeepSkyBlue); + public static Brush DimGray => GetBrush(s_dimGrayKey, Color.DimGray); + public static Brush DodgerBlue => GetBrush(s_dodgerBlueKey, Color.DodgerBlue); + + public static Brush Firebrick => GetBrush(s_firebrickKey, Color.Firebrick); + public static Brush FloralWhite => GetBrush(s_floralWhiteKey, Color.FloralWhite); + public static Brush ForestGreen => GetBrush(s_forestGreenKey, Color.ForestGreen); + public static Brush Fuchsia => GetBrush(s_fuchsiaKey, Color.Fuchsia); + + public static Brush Gainsboro => GetBrush(s_gainsboroKey, Color.Gainsboro); + public static Brush GhostWhite => GetBrush(s_ghostWhiteKey, Color.GhostWhite); + public static Brush Gold => GetBrush(s_goldKey, Color.Gold); + public static Brush Goldenrod => GetBrush(s_goldenrodKey, Color.Goldenrod); + public static Brush Gray => GetBrush(s_grayKey, Color.Gray); + public static Brush Green => GetBrush(s_greenKey, Color.Green); + public static Brush GreenYellow => GetBrush(s_greenYellowKey, Color.GreenYellow); + + public static Brush Honeydew => GetBrush(s_honeydewKey, Color.Honeydew); + public static Brush HotPink => GetBrush(s_hotPinkKey, Color.HotPink); + + public static Brush IndianRed => GetBrush(s_indianRedKey, Color.IndianRed); + public static Brush Indigo => GetBrush(s_indigoKey, Color.Indigo); + public static Brush Ivory => GetBrush(s_ivoryKey, Color.Ivory); + + public static Brush Khaki => GetBrush(s_khakiKey, Color.Khaki); + + public static Brush Lavender => GetBrush(s_lavenderKey, Color.Lavender); + public static Brush LavenderBlush => GetBrush(s_lavenderBlushKey, Color.LavenderBlush); + public static Brush LawnGreen => GetBrush(s_lawnGreenKey, Color.LawnGreen); + public static Brush LemonChiffon => GetBrush(s_lemonChiffonKey, Color.LemonChiffon); + public static Brush LightBlue => GetBrush(s_lightBlueKey, Color.LightBlue); + public static Brush LightCoral => GetBrush(s_lightCoralKey, Color.LightCoral); + public static Brush LightCyan => GetBrush(s_lightCyanKey, Color.LightCyan); + public static Brush LightGoldenrodYellow => GetBrush(s_lightGoldenrodYellowKey, Color.LightGoldenrodYellow); + public static Brush LightGreen => GetBrush(s_lightGreenKey, Color.LightGreen); + public static Brush LightGray => GetBrush(s_lightGrayKey, Color.LightGray); + public static Brush LightPink => GetBrush(s_lightPinkKey, Color.LightPink); + public static Brush LightSalmon => GetBrush(s_lightSalmonKey, Color.LightSalmon); + public static Brush LightSeaGreen => GetBrush(s_lightSeaGreenKey, Color.LightSeaGreen); + public static Brush LightSkyBlue => GetBrush(s_lightSkyBlueKey, Color.LightSkyBlue); + public static Brush LightSlateGray => GetBrush(s_lightSlateGrayKey, Color.LightSlateGray); + public static Brush LightSteelBlue => GetBrush(s_lightSteelBlueKey, Color.LightSteelBlue); + public static Brush LightYellow => GetBrush(s_lightYellowKey, Color.LightYellow); + public static Brush Lime => GetBrush(s_limeKey, Color.Lime); + public static Brush LimeGreen => GetBrush(s_limeGreenKey, Color.LimeGreen); + public static Brush Linen => GetBrush(s_linenKey, Color.Linen); + + public static Brush Magenta => GetBrush(s_magentaKey, Color.Magenta); + public static Brush Maroon => GetBrush(s_maroonKey, Color.Maroon); + public static Brush MediumAquamarine => GetBrush(s_mediumAquamarineKey, Color.MediumAquamarine); + public static Brush MediumBlue => GetBrush(s_mediumBlueKey, Color.MediumBlue); + public static Brush MediumOrchid => GetBrush(s_mediumOrchidKey, Color.MediumOrchid); + public static Brush MediumPurple => GetBrush(s_mediumPurpleKey, Color.MediumPurple); + public static Brush MediumSeaGreen => GetBrush(s_mediumSeaGreenKey, Color.MediumSeaGreen); + public static Brush MediumSlateBlue => GetBrush(s_mediumSlateBlueKey, Color.MediumSlateBlue); + public static Brush MediumSpringGreen => GetBrush(s_mediumSpringGreenKey, Color.MediumSpringGreen); + public static Brush MediumTurquoise => GetBrush(s_mediumTurquoiseKey, Color.MediumTurquoise); + public static Brush MediumVioletRed => GetBrush(s_mediumVioletRedKey, Color.MediumVioletRed); + public static Brush MidnightBlue => GetBrush(s_midnightBlueKey, Color.MidnightBlue); + public static Brush MintCream => GetBrush(s_mintCreamKey, Color.MintCream); + public static Brush MistyRose => GetBrush(s_mistyRoseKey, Color.MistyRose); + public static Brush Moccasin => GetBrush(s_moccasinKey, Color.Moccasin); + + public static Brush NavajoWhite => GetBrush(s_navajoWhiteKey, Color.NavajoWhite); + public static Brush Navy => GetBrush(s_navyKey, Color.Navy); + + public static Brush OldLace => GetBrush(s_oldLaceKey, Color.OldLace); + public static Brush Olive => GetBrush(s_oliveKey, Color.Olive); + public static Brush OliveDrab => GetBrush(s_oliveDrabKey, Color.OliveDrab); + public static Brush Orange => GetBrush(s_orangeKey, Color.Orange); + public static Brush OrangeRed => GetBrush(s_orangeRedKey, Color.OrangeRed); + public static Brush Orchid => GetBrush(s_orchidKey, Color.Orchid); + + public static Brush PaleGoldenrod => GetBrush(s_paleGoldenrodKey, Color.PaleGoldenrod); + public static Brush PaleGreen => GetBrush(s_paleGreenKey, Color.PaleGreen); + public static Brush PaleTurquoise => GetBrush(s_paleTurquoiseKey, Color.PaleTurquoise); + public static Brush PaleVioletRed => GetBrush(s_paleVioletRedKey, Color.PaleVioletRed); + public static Brush PapayaWhip => GetBrush(s_papayaWhipKey, Color.PapayaWhip); + public static Brush PeachPuff => GetBrush(s_peachPuffKey, Color.PeachPuff); + public static Brush Peru => GetBrush(s_peruKey, Color.Peru); + public static Brush Pink => GetBrush(s_pinkKey, Color.Pink); + public static Brush Plum => GetBrush(s_plumKey, Color.Plum); + public static Brush PowderBlue => GetBrush(s_powderBlueKey, Color.PowderBlue); + public static Brush Purple => GetBrush(s_purpleKey, Color.Purple); + + public static Brush Red => GetBrush(s_redKey, Color.Red); + public static Brush RosyBrown => GetBrush(s_rosyBrownKey, Color.RosyBrown); + public static Brush RoyalBlue => GetBrush(s_royalBlueKey, Color.RoyalBlue); + + public static Brush SaddleBrown => GetBrush(s_saddleBrownKey, Color.SaddleBrown); + public static Brush Salmon => GetBrush(s_salmonKey, Color.Salmon); + public static Brush SandyBrown => GetBrush(s_sandyBrownKey, Color.SandyBrown); + public static Brush SeaGreen => GetBrush(s_seaGreenKey, Color.SeaGreen); + public static Brush SeaShell => GetBrush(s_seaShellKey, Color.SeaShell); + public static Brush Sienna => GetBrush(s_siennaKey, Color.Sienna); + public static Brush Silver => GetBrush(s_silverKey, Color.Silver); + public static Brush SkyBlue => GetBrush(s_skyBlueKey, Color.SkyBlue); + public static Brush SlateBlue => GetBrush(s_slateBlueKey, Color.SlateBlue); + public static Brush SlateGray => GetBrush(s_slateGrayKey, Color.SlateGray); + public static Brush Snow => GetBrush(s_snowKey, Color.Snow); + public static Brush SpringGreen => GetBrush(s_springGreenKey, Color.SpringGreen); + public static Brush SteelBlue => GetBrush(s_steelBlueKey, Color.SteelBlue); + + public static Brush Tan => GetBrush(s_tanKey, Color.Tan); + public static Brush Teal => GetBrush(s_tealKey, Color.Teal); + public static Brush Thistle => GetBrush(s_thistleKey, Color.Thistle); + public static Brush Tomato => GetBrush(s_tomatoKey, Color.Tomato); + public static Brush Turquoise => GetBrush(s_turquoiseKey, Color.Turquoise); + + public static Brush Violet => GetBrush(s_violetKey, Color.Violet); + + public static Brush Wheat => GetBrush(s_wheatKey, Color.Wheat); + public static Brush White => GetBrush(s_whiteKey, Color.White); + public static Brush WhiteSmoke => GetBrush(s_whiteSmokeKey, Color.WhiteSmoke); + + public static Brush Yellow => GetBrush(s_yellowKey, Color.Yellow); + public static Brush YellowGreen => GetBrush(s_yellowGreenKey, Color.YellowGreen); + + private static Brush GetBrush(object key, Color color) + { + Brush? brush = (Brush?)Gdip.ThreadData[key]; + if (brush == null) + { + brush = new SolidBrush(color); + Gdip.ThreadData[key] = brush; + } + return brush; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BufferedGraphics.cs b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphics.cs new file mode 100644 index 00000000000..4224959597a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphics.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// The BufferedGraphics class can be thought of as a "Token" or "Reference" to the buffer that a + /// BufferedGraphicsContext creates. While a BufferedGraphics is outstanding, the memory associated with the + /// buffer is locked. The general design is such that under normal conditions a single BufferedGraphics will be in + /// use at one time for a given BufferedGraphicsContext. + /// + public sealed class BufferedGraphics : IDisposable + { + private readonly Graphics? _targetGraphics; + private readonly IntPtr _targetDC; + private Graphics _bufferedGraphicsSurface; + private BufferedGraphicsContext _context; + private readonly Point _targetLoc; + private readonly Size _virtualSize; + + /// + /// Internal constructor, this class is created by BufferedGraphicsContext. + /// + internal BufferedGraphics(Graphics bufferedGraphicsSurface, BufferedGraphicsContext context, Graphics? targetGraphics, + IntPtr targetDC, Point targetLoc, Size virtualSize) + { + _context = context; + _bufferedGraphicsSurface = bufferedGraphicsSurface; + _targetDC = targetDC; + _targetGraphics = targetGraphics; + _targetLoc = targetLoc; + _virtualSize = virtualSize; + } + + public void Dispose() + { + if (_context != null) + { + _context.ReleaseBuffer(); + + if (DisposeContext) + { + _context.Dispose(); + _context = null!; + } + } + + if (_bufferedGraphicsSurface != null) + { + _bufferedGraphicsSurface.Dispose(); + _bufferedGraphicsSurface = null!; + } + } + + /// + /// Allows access to the Graphics wrapper for the buffer. + /// + public Graphics Graphics => _bufferedGraphicsSurface; + + /// + /// Renders the buffer to the specified target graphics. + /// + public void Render(Graphics? target) + { + if (target != null) + { + IntPtr targetDC = target.GetHdc(); + + try + { + RenderInternal(new HandleRef(target, targetDC)); + } + finally + { + target.ReleaseHdcInternal(targetDC); + } + } + } + + /// + /// Internal method that renders the specified buffer into the target. + /// + private void RenderInternal(HandleRef refTargetDC) + { + IntPtr sourceDC = Graphics.GetHdc(); + + try + { + Interop.Gdi32.BitBlt( + refTargetDC, + _targetLoc.X, + _targetLoc.Y, + _virtualSize.Width, + _virtualSize.Height, + new HandleRef(Graphics, sourceDC), + 0, + 0, + Interop.Gdi32.RasterOp.SRCCOPY); + } + finally + { + Graphics.ReleaseHdcInternal(sourceDC); + } + } + + /// + /// Determines if we need to dispose of the Context when this is disposed. + /// + internal bool DisposeContext { get; set; } + + /// + /// Renders the buffer to the original graphics used to allocate the buffer. + /// + public void Render() + { + if (_targetGraphics != null) + { + Render(_targetGraphics); + } + else + { + RenderInternal(new HandleRef(Graphics, _targetDC)); + } + } + + /// + /// Renders the buffer to the specified target HDC. + /// + public void Render(IntPtr targetDC) => RenderInternal(new HandleRef(null, targetDC)); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsContext.cs b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsContext.cs new file mode 100644 index 00000000000..22e9eb0b9b3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsContext.cs @@ -0,0 +1,542 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace System.Drawing +{ + /// + /// The BufferedGraphicsContext class can be used to perform standard double buffer rendering techniques. + /// + public sealed class BufferedGraphicsContext : IDisposable + { + private Size _maximumBuffer; + private Size _bufferSize = Size.Empty; + private Size _virtualSize; + private Point _targetLoc; + private IntPtr _compatDC; + private IntPtr _dib; + private IntPtr _oldBitmap; + private Graphics? _compatGraphics; + private BufferedGraphics? _buffer; + private int _busy; + private bool _invalidateWhenFree; + + private const int BufferFree = 0; // The graphics buffer is free to use. + private const int BufferBusyPainting = 1; // The graphics buffer is busy being created/painting. + private const int BufferBusyDisposing = 2; // The graphics buffer is busy disposing. + + /// + /// Basic constructor. + /// + public BufferedGraphicsContext() + { + // By defualt, the size of our maxbuffer will be 3 x standard button size. + _maximumBuffer.Width = 75 * 3; + _maximumBuffer.Height = 32 * 3; + } + + /// + /// Allows you to set the maximum width and height of the buffer that will be retained in memory. + /// You can allocate a buffer of any size, however any request for a buffer that would have a total + /// memory footprint larger that the maximum size will be allocated temporarily and then discarded + /// with the BufferedGraphics is released. + /// + public Size MaximumBuffer + { + get => _maximumBuffer; + set + { + if (value.Width <= 0 || value.Height <= 0) + { + throw new ArgumentException(SR.Format(SR.InvalidArgumentValue, nameof(MaximumBuffer), value), nameof(value)); + } + + // If we've been asked to decrease the size of the maximum buffer, + // then invalidate the older & larger buffer. + if (value.Width * value.Height < _maximumBuffer.Width * _maximumBuffer.Height) + { + Invalidate(); + } + + _maximumBuffer = value; + } + } + + ~BufferedGraphicsContext() => Dispose(false); + + /// + /// Returns a BufferedGraphics that is matched for the specified target Graphics object. + /// + public BufferedGraphics Allocate(Graphics targetGraphics, Rectangle targetRectangle) + { + if (ShouldUseTempManager(targetRectangle)) + { + return AllocBufferInTempManager(targetGraphics, IntPtr.Zero, targetRectangle); + } + + return AllocBuffer(targetGraphics, IntPtr.Zero, targetRectangle); + } + + /// + /// Returns a BufferedGraphics that is matched for the specified target HDC object. + /// + public BufferedGraphics Allocate(IntPtr targetDC, Rectangle targetRectangle) + { + if (ShouldUseTempManager(targetRectangle)) + { + return AllocBufferInTempManager(null, targetDC, targetRectangle); + } + + return AllocBuffer(null, targetDC, targetRectangle); + } + + /// + /// Returns a BufferedGraphics that is matched for the specified target HDC object. + /// + private BufferedGraphics AllocBuffer(Graphics? targetGraphics, IntPtr targetDC, Rectangle targetRectangle) + { + int oldBusy = Interlocked.CompareExchange(ref _busy, BufferBusyPainting, BufferFree); + + // In the case were we have contention on the buffer - i.e. two threads + // trying to use the buffer at the same time, we just create a temp + // buffermanager and have the buffer dispose of it when it is done. + // + if (oldBusy != BufferFree) + { + return AllocBufferInTempManager(targetGraphics, targetDC, targetRectangle); + } + + Graphics surface; + _targetLoc = new Point(targetRectangle.X, targetRectangle.Y); + + try + { + if (targetGraphics != null) + { + IntPtr destDc = targetGraphics.GetHdc(); + try + { + surface = CreateBuffer(destDc, targetRectangle.Width, targetRectangle.Height); + } + finally + { + targetGraphics.ReleaseHdcInternal(destDc); + } + } + else + { + surface = CreateBuffer(targetDC, targetRectangle.Width, targetRectangle.Height); + } + + _buffer = new BufferedGraphics(surface, this, targetGraphics, targetDC, _targetLoc, _virtualSize); + } + catch + { + // Free the buffer so it can be disposed. + _busy = BufferFree; + throw; + } + + return _buffer; + } + + /// + /// Returns a BufferedGraphics that is matched for the specified target HDC object. + /// + private static BufferedGraphics AllocBufferInTempManager(Graphics? targetGraphics, IntPtr targetDC, Rectangle targetRectangle) + { + BufferedGraphicsContext? tempContext = null; + BufferedGraphics? tempBuffer = null; + + try + { + tempContext = new BufferedGraphicsContext(); + tempBuffer = tempContext.AllocBuffer(targetGraphics, targetDC, targetRectangle); + tempBuffer.DisposeContext = true; + } + finally + { + if (tempContext != null && (tempBuffer == null || (tempBuffer != null && !tempBuffer.DisposeContext))) + { + tempContext.Dispose(); + } + } + + return tempBuffer; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// This routine allows us to control the point were we start using throw away + /// managers for painting. Since the buffer manager stays around (by default) + /// for the life of the app, we don't want to consume too much memory + /// in the buffer. However, re-allocating the buffer for small things (like + /// buttons, labels, etc) will hit us on runtime performance. + /// + private bool ShouldUseTempManager(Rectangle targetBounds) + { + return (targetBounds.Width * targetBounds.Height) > (MaximumBuffer.Width * MaximumBuffer.Height); + } + + /// + /// Fills in the fields of a BITMAPINFO so that we can create a bitmap + /// that matches the format of the display. + /// + /// This is done by creating a compatible bitmap and calling GetDIBits + /// to return the color masks. This is done with two calls. The first + /// call passes in biBitCount = 0 to GetDIBits which will fill in the + /// base BITMAPINFOHEADER data. The second call to GetDIBits (passing + /// in the BITMAPINFO filled in by the first call) will return the color + /// table or bitmasks, as appropriate. + /// + /// True if successful, false otherwise. + private unsafe bool FillBitmapInfo(IntPtr hdc, IntPtr hpal, ref Interop.Gdi32.BITMAPINFO_FLAT pbmi) + { + IntPtr hbm = IntPtr.Zero; + bool bRet = false; + try + { + // Create a dummy bitmap from which we can query color format info + // about the device surface. + hbm = Interop.Gdi32.CreateCompatibleBitmap(new HandleRef(null, hdc), 1, 1); + + if (hbm == IntPtr.Zero) + { + throw new OutOfMemoryException(SR.GraphicsBufferQueryFail); + } + + pbmi.bmiHeader_biSize = sizeof(NativeMethods.BITMAPINFOHEADER); + + // Call first time to fill in BITMAPINFO header. + Interop.Gdi32.GetDIBits(new HandleRef(null, hdc), + new HandleRef(null, hbm), + 0, + 0, + IntPtr.Zero, + ref pbmi, + NativeMethods.DIB_RGB_COLORS); + + if (pbmi.bmiHeader_biBitCount <= 8) + { + bRet = FillColorTable(hdc, hpal, ref pbmi); + } + else + { + if (pbmi.bmiHeader_biCompression == NativeMethods.BI_BITFIELDS) + { + // Call a second time to get the color masks. + Interop.Gdi32.GetDIBits(new HandleRef(null, hdc), + new HandleRef(null, hbm), + 0, + pbmi.bmiHeader_biHeight, + IntPtr.Zero, + ref pbmi, + NativeMethods.DIB_RGB_COLORS); + } + bRet = true; + } + } + finally + { + if (hbm != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(hbm); + } + } + return bRet; + } + + /// + /// Initialize the color table of the BITMAPINFO pointed to by pbmi. Colors + /// are set to the current system palette. + /// + /// Note: call only valid for displays of 8bpp or less. + /// + /// True is successful, false otherwise. + private unsafe bool FillColorTable(IntPtr hdc, IntPtr hpal, ref Interop.Gdi32.BITMAPINFO_FLAT pbmi) + { + byte[] aj = new byte[sizeof(NativeMethods.PALETTEENTRY) * 256]; + + fixed (byte* pcolors = pbmi.bmiColors) + { + fixed (byte* ppal = aj) + { + NativeMethods.RGBQUAD* prgb = (NativeMethods.RGBQUAD*)pcolors; + NativeMethods.PALETTEENTRY* lppe = (NativeMethods.PALETTEENTRY*)ppal; + + int cColors = 1 << pbmi.bmiHeader_biBitCount; + if (cColors <= 256) + { + // Note: we don't support 4bpp displays. + uint palRet; + IntPtr palHalftone = IntPtr.Zero; + if (hpal == IntPtr.Zero) + { + palHalftone = Graphics.GetHalftonePalette(); + palRet = Interop.Gdi32.GetPaletteEntries(new HandleRef(null, palHalftone), 0, cColors, aj); + } + else + { + palRet = Interop.Gdi32.GetPaletteEntries(new HandleRef(null, hpal), 0, cColors, aj); + } + + if (palRet != 0) + { + for (int i = 0; i < cColors; i++) + { + prgb[i].rgbRed = lppe[i].peRed; + prgb[i].rgbGreen = lppe[i].peGreen; + prgb[i].rgbBlue = lppe[i].peBlue; + prgb[i].rgbReserved = 0; + } + + return true; + } + } + } + } + + return false; + } + + /// + /// Returns a Graphics object representing a buffer. + /// + private Graphics CreateBuffer(IntPtr src, int width, int height) + { + // Create the compat DC. + _busy = BufferBusyDisposing; + DisposeDC(); + _busy = BufferBusyPainting; + _compatDC = Interop.Gdi32.CreateCompatibleDC(src); + + // Recreate the bitmap if necessary. + if (width > _bufferSize.Width || height > _bufferSize.Height) + { + int optWidth = Math.Max(width, _bufferSize.Width); + int optHeight = Math.Max(height, _bufferSize.Height); + + _busy = BufferBusyDisposing; + DisposeBitmap(); + _busy = BufferBusyPainting; + + IntPtr pvbits = IntPtr.Zero; + _dib = CreateCompatibleDIB(src, IntPtr.Zero, optWidth, optHeight, ref pvbits); + _bufferSize = new Size(optWidth, optHeight); + } + + // Select the bitmap. + _oldBitmap = Interop.Kernel32.SelectObject(new HandleRef(this, _compatDC), new HandleRef(this, _dib)); + + // Create compat graphics. + _compatGraphics = Graphics.FromHdcInternal(_compatDC); + _compatGraphics.TranslateTransform(-_targetLoc.X, -_targetLoc.Y); + _virtualSize = new Size(width, height); + + return _compatGraphics; + } + + /// + /// Create a DIB section with an optimal format w.r.t. the specified hdc. + /// + /// If DIB <= 8bpp, then the DIB color table is initialized based on the + /// specified palette. If the palette handle is NULL, then the system + /// palette is used. + /// + /// Note: The hdc must be a direct DC (not an info or memory DC). + /// + /// Note: On palettized displays, if the system palette changes the + /// UpdateDIBColorTable function should be called to maintain + /// the identity palette mapping between the DIB and the display. + /// + /// A valid bitmap handle if successful, IntPtr.Zero otherwise. + private IntPtr CreateCompatibleDIB(IntPtr hdc, IntPtr hpal, int ulWidth, int ulHeight, ref IntPtr ppvBits) + { + if (hdc == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(hdc)); + } + + IntPtr hbmRet = IntPtr.Zero; + Interop.Gdi32.BITMAPINFO_FLAT pbmi = default; + + // Validate hdc. + Interop.Gdi32.ObjectType objType = Interop.Gdi32.GetObjectType(hdc); + switch (objType) + { + case Interop.Gdi32.ObjectType.OBJ_DC: + case Interop.Gdi32.ObjectType.OBJ_METADC: + case Interop.Gdi32.ObjectType.OBJ_MEMDC: + case Interop.Gdi32.ObjectType.OBJ_ENHMETADC: + break; + default: + throw new ArgumentException(SR.DCTypeInvalid); + } + + if (FillBitmapInfo(hdc, hpal, ref pbmi)) + { + // Change bitmap size to match specified dimensions. + pbmi.bmiHeader_biWidth = ulWidth; + pbmi.bmiHeader_biHeight = ulHeight; + if (pbmi.bmiHeader_biCompression == NativeMethods.BI_RGB) + { + pbmi.bmiHeader_biSizeImage = 0; + } + else + { + if (pbmi.bmiHeader_biBitCount == 16) + { + pbmi.bmiHeader_biSizeImage = ulWidth * ulHeight * 2; + } + else if (pbmi.bmiHeader_biBitCount == 32) + { + pbmi.bmiHeader_biSizeImage = ulWidth * ulHeight * 4; + } + else + { + pbmi.bmiHeader_biSizeImage = 0; + } + } + pbmi.bmiHeader_biClrUsed = 0; + pbmi.bmiHeader_biClrImportant = 0; + + // Create the DIB section. Let Win32 allocate the memory and return + // a pointer to the bitmap surface. + hbmRet = Interop.Gdi32.CreateDIBSection(new HandleRef(null, hdc), ref pbmi, NativeMethods.DIB_RGB_COLORS, ref ppvBits, IntPtr.Zero, 0); + Win32Exception? ex = null; + if (hbmRet == IntPtr.Zero) + { + ex = new Win32Exception(Marshal.GetLastWin32Error()); + } + + if (ex != null) + { + throw ex; + } + } + + return hbmRet; + } + + /// + /// Disposes the DC, but leaves the bitmap alone. + /// + private void DisposeDC() + { + if (_oldBitmap != IntPtr.Zero && _compatDC != IntPtr.Zero) + { + Interop.Kernel32.SelectObject(new HandleRef(this, _compatDC), new HandleRef(this, _oldBitmap)); + _oldBitmap = IntPtr.Zero; + } + + if (_compatDC != IntPtr.Zero) + { + Interop.Gdi32.DeleteDC(new HandleRef(this, _compatDC)); + _compatDC = IntPtr.Zero; + } + } + + /// + /// Disposes the bitmap, will ASSERT if bitmap is being used (checks oldbitmap). if ASSERTed, call DisposeDC() first. + /// + private void DisposeBitmap() + { + if (_dib != IntPtr.Zero) + { + Debug.Assert(_oldBitmap == IntPtr.Zero); + + Interop.Gdi32.DeleteObject(new HandleRef(this, _dib)); + _dib = IntPtr.Zero; + } + } + + /// + /// Disposes of the Graphics buffer. + /// + private void Dispose(bool disposing) + { + int oldBusy = Interlocked.CompareExchange(ref _busy, BufferBusyDisposing, BufferFree); + + if (disposing) + { + if (oldBusy == BufferBusyPainting) + { + throw new InvalidOperationException(SR.GraphicsBufferCurrentlyBusy); + } + + if (_compatGraphics != null) + { + _compatGraphics.Dispose(); + _compatGraphics = null; + } + } + + DisposeDC(); + DisposeBitmap(); + + if (_buffer != null) + { + _buffer.Dispose(); + _buffer = null; + } + + _bufferSize = Size.Empty; + _virtualSize = Size.Empty; + + _busy = BufferFree; + } + + /// + /// Invalidates the cached graphics buffer. + /// + public void Invalidate() + { + int oldBusy = Interlocked.CompareExchange(ref _busy, BufferBusyDisposing, BufferFree); + + // If we're not busy with our buffer, lets clean it up now + if (oldBusy == BufferFree) + { + Dispose(); + _busy = BufferFree; + } + else + { + // This will indicate to free the buffer as soon as it becomes non-busy. + _invalidateWhenFree = true; + } + } + + /// + /// Returns a Graphics object representing a buffer. + /// + internal void ReleaseBuffer() + { + _buffer = null; + if (_invalidateWhenFree) + { + // Clears everything including the bitmap. + _busy = BufferBusyDisposing; + Dispose(); + } + else + { + // Otherwise, just dispose the DC. A new one will be created next time. + _busy = BufferBusyDisposing; + + // Only clears out the DC. + DisposeDC(); + } + + _busy = BufferFree; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsManager.cs b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsManager.cs new file mode 100644 index 00000000000..5612b92e732 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/BufferedGraphicsManager.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.ConstrainedExecution; + +namespace System.Drawing +{ + /// + /// The BufferedGraphicsManager is used for accessing a BufferedGraphicsContext. + /// + public static class BufferedGraphicsManager + { + /// + /// Static constructor. Here, we hook the exit & unload events so we can clean up our context buffer. + /// + static BufferedGraphicsManager() + { + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnShutdown); + AppDomain.CurrentDomain.DomainUnload += new EventHandler(OnShutdown); + Current = new BufferedGraphicsContext(); + } + + /// + /// Retrieves the context associated with the app domain. + /// + public static BufferedGraphicsContext Current { get; } + + /// + /// Called on process exit + /// + private static void OnShutdown(object? sender, EventArgs e) => Current.Invalidate(); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/CharacterRange.cs b/src/System.Drawing.Common/src/System/Drawing/CharacterRange.cs new file mode 100644 index 00000000000..a0dccdc4b28 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/CharacterRange.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + [StructLayout(LayoutKind.Sequential)] + public struct CharacterRange : IEquatable + { + private int _first; + private int _length; + + /// Initializes a new instance of the class with the specified coordinates. + public CharacterRange(int First, int Length) + { + _first = First; + _length = Length; + } + + /// Gets the First character position of this . + public int First + { + get => _first; + set => _first = value; + } + + /// Gets the Length of this . + public int Length + { + get => _length; + set => _length = value; + } + + public override bool Equals([NotNullWhen(true)] object? obj) => + obj is CharacterRange other && Equals(other); + + /// Indicates whether the current instance is equal to another instance of the same type. + /// An instance to compare with this instance. + /// true if the current instance is equal to the other instance; otherwise, false. + public bool Equals(CharacterRange other) => First == other.First && Length == other.Length; + + public static bool operator ==(CharacterRange cr1, CharacterRange cr2) => cr1.Equals(cr2); + + public static bool operator !=(CharacterRange cr1, CharacterRange cr2) => !cr1.Equals(cr2); + + public override int GetHashCode() => HashCode.Combine(First, Length); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ClientUtils.cs b/src/System.Drawing.Common/src/System/Drawing/ClientUtils.cs new file mode 100644 index 00000000000..ddaab963c78 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ClientUtils.cs @@ -0,0 +1,275 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Security; + +namespace System.Drawing +{ + internal static class ClientUtils + { + // ExecutionEngineException is obsolete and shouldn't be used (to catch, throw or reference) anymore. + // Pragma added to prevent converting the "type is obsolete" warning into build error. +#pragma warning disable 618 + public static bool IsCriticalException(Exception ex) + { + return ex is NullReferenceException + || ex is StackOverflowException + || ex is OutOfMemoryException + || ex is System.Threading.ThreadAbortException + || ex is ExecutionEngineException + || ex is IndexOutOfRangeException + || ex is AccessViolationException; + } +#pragma warning restore 618 + + public static bool IsSecurityOrCriticalException(Exception ex) + { + return (ex is SecurityException) || IsCriticalException(ex); + } + + /// + /// WeakRefCollection - a collection that holds onto weak references. + /// + /// Essentially you pass in the object as it is, and under the covers + /// we only hold a weak reference to the object. + /// + /// ----------------------------------------------------------------- + /// !!!IMPORTANT USAGE NOTE!!! + /// Users of this class should set the RefCheckThreshold property + /// explicitly or call ScavengeReferences every once in a while to + /// remove dead references. + /// Also avoid calling Remove(item). Instead call RemoveByHashCode(item) + /// to make sure dead refs are removed. + /// + internal sealed class WeakRefCollection : IList + { + internal WeakRefCollection() : this(4) { } + + internal WeakRefCollection(int size) => InnerList = new ArrayList(size); + + internal ArrayList InnerList { get; } + + /// + /// Indicates the value where the collection should check its items to remove dead weakref left over. + /// Note: When GC collects weak refs from this collection the WeakRefObject identity changes since its + /// Target becomes null. This makes the item unrecognizable by the collection and cannot be + /// removed - Remove(item) and Contains(item) will not find it anymore. + /// A value of int.MaxValue means disabled by default. + /// + public int RefCheckThreshold { get; set; } = int.MaxValue; + + public object? this[int index] + { + get + { + if (InnerList[index] is WeakRefObject weakRef && weakRef.IsAlive) + { + return weakRef.Target; + } + + return null; + } + set => InnerList[index] = CreateWeakRefObject(value); + } + + public void ScavengeReferences() + { + int currentIndex = 0; + int currentCount = Count; + for (int i = 0; i < currentCount; i++) + { + object? item = this[currentIndex]; + + if (item == null) + { + InnerList.RemoveAt(currentIndex); + } + else + { + // Only incriment if we have not removed the item. + currentIndex++; + } + } + } + + public override bool Equals(object? obj) + { + if (!(obj is WeakRefCollection other)) + { + return false; + } + + if (other == null || Count != other.Count) + { + return false; + } + + for (int i = 0; i < Count; i++) + { + object? thisObj = InnerList[i]; + object? otherObj = other.InnerList[i]; + if (thisObj != otherObj) + { + if (thisObj is null || !thisObj.Equals(otherObj)) + { + return false; + } + } + } + + return true; + } + + public override int GetHashCode() => base.GetHashCode(); + + [return: NotNullIfNotNull(nameof(value))] + private static WeakRefObject? CreateWeakRefObject(object? value) + { + if (value == null) + { + return null; + } + + return new WeakRefObject(value); + } + + private static void Copy(WeakRefCollection sourceList, int sourceIndex, WeakRefCollection destinationList, int destinationIndex, int length) + { + if (sourceIndex < destinationIndex) + { + // We need to copy from the back forward to prevent overwrite if source and + // destination lists are the same, so we need to flip the source/dest indices + // to point at the end of the spans to be copied. + sourceIndex += length; + destinationIndex += length; + for (; length > 0; length--) + { + destinationList.InnerList[--destinationIndex] = sourceList.InnerList[--sourceIndex]; + } + } + else + { + for (; length > 0; length--) + { + destinationList.InnerList[destinationIndex++] = sourceList.InnerList[sourceIndex++]; + } + } + } + + /// + /// Removes the value using its hash code as its identity. + /// This is needed because the underlying item in the collection may have already been collected changing + /// the identity of the WeakRefObject making it impossible for the collection to identify it. + /// See WeakRefObject for more info. + /// + public void RemoveByHashCode(object value) + { + if (value == null) + { + return; + } + + int hash = value.GetHashCode(); + + for (int idx = 0; idx < InnerList.Count; idx++) + { + if (InnerList[idx] != null && InnerList[idx]!.GetHashCode() == hash) + { + RemoveAt(idx); + return; + } + } + } + + public void Clear() => InnerList.Clear(); + + public bool IsFixedSize => InnerList.IsFixedSize; + + public bool Contains(object? value) => InnerList.Contains(CreateWeakRefObject(value)); + + public void RemoveAt(int index) => InnerList.RemoveAt(index); + + public void Remove(object? value) => InnerList.Remove(CreateWeakRefObject(value)); + + public int IndexOf(object? value) => InnerList.IndexOf(CreateWeakRefObject(value)); + + public void Insert(int index, object? value) => InnerList.Insert(index, CreateWeakRefObject(value)); + + public int Add(object? value) + { + if (Count > RefCheckThreshold) + { + ScavengeReferences(); + } + + return InnerList.Add(CreateWeakRefObject(value)); + } + + public int Count => InnerList.Count; + + object ICollection.SyncRoot => InnerList.SyncRoot; + + public bool IsReadOnly => InnerList.IsReadOnly; + + public void CopyTo(Array array, int index) => InnerList.CopyTo(array, index); + + bool ICollection.IsSynchronized => InnerList.IsSynchronized; + + public IEnumerator GetEnumerator() => InnerList.GetEnumerator(); + + /// + /// Wraps a weak ref object. + /// WARNING: Use this class carefully! + /// When the weak ref is collected, this object looses its identity. This is bad when the object has been + /// added to a collection since Contains(WeakRef(item)) and Remove(WeakRef(item)) would not be able to + /// identify the item. + /// + internal sealed class WeakRefObject + { + private readonly int _hash; + private readonly WeakReference _weakHolder; + + internal WeakRefObject(object obj) + { + Debug.Assert(obj != null, "Unexpected null object!"); + _weakHolder = new WeakReference(obj); + _hash = obj.GetHashCode(); + } + + internal bool IsAlive => _weakHolder.IsAlive; + + internal object? Target => _weakHolder.Target; + + public override int GetHashCode() => _hash; + + public override bool Equals(object? obj) + { + WeakRefObject? other = obj as WeakRefObject; + + if (other == this) + { + return true; + } + + if (other == null) + { + return false; + } + + if (other.Target != Target) + { + if (Target == null || !Target.Equals(other.Target)) + { + return false; + } + } + + return true; + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ColorConverter.cs b/src/System.Drawing.Common/src/System/Drawing/ColorConverter.cs new file mode 100644 index 00000000000..eadb0ea57ea --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ColorConverter.cs @@ -0,0 +1,410 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Reflection; + using System.Threading; + + /// + /// + /// ColorConverter is a class that can be used to convert + /// colors from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class ColorConverter : TypeConverter { + private static string ColorConstantsLock = "colorConstants"; + private static Hashtable colorConstants; + private static string SystemColorConstantsLock = "systemColorConstants"; + private static Hashtable systemColorConstants; + private static string ValuesLock = "values"; + private static StandardValuesCollection values; + + /// + /// + /// [To be supplied.] + /// + public ColorConverter() { + } + + /// + /// + /// Hashtable of color / value pairs (color name is key) + /// for standard colors. + /// + private static Hashtable Colors { + get { + if (colorConstants == null) { + lock(ColorConstantsLock) { + if (colorConstants == null) { + Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase); + FillConstants(tempHash, typeof(Color)); + colorConstants = tempHash; + } + } + } + + return colorConstants; + } + } + + /// + /// + /// Hashtable of color / value pairs (color name is key) + /// for system colors. + /// + private static Hashtable SystemColors { + get { + if (systemColorConstants == null) { + lock (SystemColorConstantsLock) { + if (systemColorConstants == null) { + Hashtable tempHash = new Hashtable(StringComparer.OrdinalIgnoreCase); + FillConstants(tempHash, typeof(System.Drawing.SystemColors)); + systemColorConstants = tempHash; + } + } + } + + return systemColorConstants; + } + } + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, [NotNullWhen(true)] Type? destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + internal static object GetNamedColor(string name) { + object color = null; + // First, check to see if this is a standard name. + // + color = Colors[name]; + if (color != null) { + return color; + } + // Ok, how about a system color? + // + color = SystemColors[name]; + return color; + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + string strValue = value as string; + if (strValue != null) { + object obj = null; + string text = strValue.Trim(); + + if (text.Length == 0) { + obj = Color.Empty; + } + else { + // First, check to see if this is a standard name. + // + obj = GetNamedColor(text); + + if (obj == null) { + culture ??= CultureInfo.CurrentCulture; + + char sep = culture.TextInfo.ListSeparator[0]; + bool tryMappingToKnownColor = true; + + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + + // If the value is a 6 digit hex number only, then + // we want to treat the Alpha as 255, not 0 + // + if (text.IndexOf(sep) == -1) { + + // text can be '' (empty quoted string) + if (text.Length >= 2 && (text[0] == '\'' || text[0] == '"') && text[0] == text[text.Length -1]) { + // In quotes means a named value + string colorName = text.Substring(1, text.Length - 2); + obj = Color.FromName(colorName); + tryMappingToKnownColor = false; + } + else if ((text.Length == 7 && text[0] == '#') || + (text.Length == 8 && (text.StartsWith("0x") || text.StartsWith("0X"))) || + (text.Length == 8 && (text.StartsWith("&h") || text.StartsWith("&H")))) { + // Note: ConvertFromString will raise exception if value cannot be converted. + obj = Color.FromArgb(unchecked((int)(0xFF000000 | (uint)(int)intConverter.ConvertFromString(context, culture, text)))); + } + } + + // Nope. Parse the RGBA from the text. + // + if (obj == null) { + string[] tokens = text.Split(sep); + int[] values = new int[tokens.Length]; + for (int i = 0; i < values.Length; i++) { + values[i] = unchecked((int)intConverter.ConvertFromString(context, culture, tokens[i])); + } + + // We should now have a number of parsed integer values. + // We support 1, 3, or 4 arguments: + // + // 1 -- full ARGB encoded + // 3 -- RGB + // 4 -- ARGB + // + switch (values.Length) { + case 1: + obj = Color.FromArgb(values[0]); + break; + + case 3: + obj = Color.FromArgb(values[0], values[1], values[2]); + break; + + case 4: + obj = Color.FromArgb(values[0], values[1], values[2], values[3]); + break; + } + tryMappingToKnownColor = true; + } + + if ((obj != null) && tryMappingToKnownColor) { + + // Now check to see if this color matches one of our known colors. + // If it does, then substitute it. We can only do this for "Colors" + // because system colors morph with user settings. + // + int targetARGB = ((Color)obj).ToArgb(); + + foreach (Color c in Colors.Values) { + if (c.ToArgb() == targetARGB) { + obj = c; + break; + } + } + } + } + + if (obj == null) { + throw new ArgumentException(SR.Format(SR.InvalidColor, text)); + } + } + return obj; + } + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the destination type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException(nameof(destinationType)); + } + + if ( value is Color ){ + if (destinationType == typeof(string)) { + Color c = (Color)value; + + if (c == Color.Empty) { + return string.Empty; + } + else { + // If this is a known color, then Color can provide its own + // name. Otherwise, we fabricate an ARGB value for it. + // + if (c.IsKnownColor) { + return c.Name; + } + else if (c.IsNamedColor) { + return "'" + c.Name + "'"; + } + else { + culture ??= CultureInfo.CurrentCulture; + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args; + int nArg = 0; + + if (c.A < 255) { + args = new string[4]; + args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.A); + } + else { + args = new string[3]; + } + + // Note: ConvertToString will raise exception if value cannot be converted. + args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.R); + args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.G); + args[nArg++] = intConverter.ConvertToString(context, culture, (object)c.B); + + // Now slam all of these together with the fantastic Join + // method. + // + return string.Join(sep, args); + } + } + } + if (destinationType == typeof(InstanceDescriptor)) { + MemberInfo member = null; + object[] args = null; + + Color c = (Color)value; + + if (c.IsEmpty) { + member = typeof(Color).GetField("Empty"); + } + else if (c.IsSystemColor) { + member = typeof(SystemColors).GetProperty(c.Name); + } + else if (c.IsKnownColor) { + member = typeof(Color).GetProperty(c.Name); + } + else if (c.A != 255) { + member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int), typeof(int)}); + args = new object[] {c.A, c.R, c.G, c.B}; + } + else if (c.IsNamedColor) { + member = typeof(Color).GetMethod("FromName", new Type[] {typeof(string)}); + args = new object[] {c.Name}; + } + else { + member = typeof(Color).GetMethod("FromArgb", new Type[] {typeof(int), typeof(int), typeof(int)}); + args = new object[] {c.R, c.G, c.B}; + } + + Debug.Assert(member != null, "Could not convert color to member. Did someone change method name / signature and not update Colorconverter?"); + if (member != null) { + return new InstanceDescriptor(member, args); + } + else { + return null; + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Fills the given hashtable with field name / value pairs. It walks all public static + /// properties of enumType that have a property type of Color. + /// + private static void FillConstants(Hashtable hash, Type enumType) { + MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.Static; + PropertyInfo[] props = enumType.GetProperties(); + + for (int i = 0; i < props.Length; i++) { + PropertyInfo prop = props[i]; + if (prop.PropertyType == typeof(Color)) { + MethodInfo method = prop.GetGetMethod(); + if (method != null && (method.Attributes & attrs) == attrs) { + object[] tempIndex = null; + hash[prop.Name] = prop.GetValue(null, tempIndex); + } + } + } + } + + /// + /// + /// Retrieves a collection containing a set of standard values + /// for the data type this validator is designed for. This + /// will return null if the data type does not support a + /// standard set of values. + /// + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + if (values == null) { + lock (ValuesLock) { + if (values == null) { + + // We must take the value from each hashtable and combine them. + // + ArrayList arrayValues = new ArrayList(); + arrayValues.AddRange(Colors.Values); + arrayValues.AddRange(SystemColors.Values); + + // Now, we have a couple of colors that have the same names but + // are identical values. Look for these and remove them. Too + // bad this is n^2. + // + int count = arrayValues.Count; + for (int i = 0; i < count - 1; i++) { + for (int j = i + 1; j < count; j++) { + if (arrayValues[i].Equals(arrayValues[j])) { + // Remove this item! + // + arrayValues.RemoveAt(j); + count--; + j--; + } + } + } + + // Sort the array. + // + arrayValues.Sort(0, arrayValues.Count, new ColorComparer()); + values = new StandardValuesCollection(arrayValues.ToArray()); + } + } + } + + return values; + } + + /// + /// + /// Determines if this object supports a standard set of values + /// that can be picked from a list. + /// + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// IComparer for color values. This takes color values but compares their + /// names. + /// + private sealed class ColorComparer : IComparer { + + public int Compare(object left, object right) { + Color cLeft = (Color)left; + Color cRight = (Color)right; + return string.Compare(cLeft.Name, cRight.Name, false, CultureInfo.InvariantCulture); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ContentAlignment.cs b/src/System.Drawing.Common/src/System/Drawing/ContentAlignment.cs new file mode 100644 index 00000000000..12a1932e19c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ContentAlignment.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies alignment of content on the drawing surface. + /// + [System.ComponentModel.Editor("System.Drawing.Design.ContentAlignmentEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public enum ContentAlignment + { + /// + /// Content is vertically aligned at the top, and horizontally aligned on the left. + /// + TopLeft = 0x001, + /// + /// Content is vertically aligned at the top, and horizontally aligned at the center. + /// + TopCenter = 0x002, + /// + /// Content is vertically aligned at the top, and horizontally aligned on the right. + /// + TopRight = 0x004, + /// + /// Content is vertically aligned in the middle, and horizontally aligned on the left. + /// + MiddleLeft = 0x010, + /// + /// Content is vertically aligned in the middle, and horizontally aligned at the center. + /// + MiddleCenter = 0x020, + /// + /// Content is vertically aligned in the middle, and horizontally aligned on the right. + /// + MiddleRight = 0x040, + /// + /// Content is vertically aligned at the bottom, and horizontally aligned on the left. + /// + BottomLeft = 0x100, + /// + /// Content is vertically aligned at the bottom, and horizontally aligned at the center. + /// + BottomCenter = 0x200, + /// + /// Content is vertically aligned at the bottom, and horizontally aligned on the right. + /// + BottomRight = 0x400, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs b/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs new file mode 100644 index 00000000000..ae66effca9e --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/CopyPixelOperation.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the Copy Pixel (ROP) operation. + /// + public enum CopyPixelOperation + { + /// + /// Fills the Destination Rectangle using the color associated with the index 0 in the physical palette. + /// + Blackness = Interop.Gdi32.RasterOp.BLACKNESS, + /// + /// Includes any windows that are Layered on Top. + /// + CaptureBlt = Interop.Gdi32.RasterOp.CAPTUREBLT, + DestinationInvert = Interop.Gdi32.RasterOp.DSTINVERT, + MergeCopy = Interop.Gdi32.RasterOp.MERGECOPY, + MergePaint = Interop.Gdi32.RasterOp.MERGEPAINT, + NoMirrorBitmap = Interop.Gdi32.RasterOp.NOMIRRORBITMAP, + NotSourceCopy = Interop.Gdi32.RasterOp.NOTSRCCOPY, + NotSourceErase = Interop.Gdi32.RasterOp.NOTSRCERASE, + PatCopy = Interop.Gdi32.RasterOp.PATCOPY, + PatInvert = Interop.Gdi32.RasterOp.PATINVERT, + PatPaint = Interop.Gdi32.RasterOp.PATPAINT, + SourceAnd = Interop.Gdi32.RasterOp.SRCAND, + SourceCopy = Interop.Gdi32.RasterOp.SRCCOPY, + SourceErase = Interop.Gdi32.RasterOp.SRCERASE, + SourceInvert = Interop.Gdi32.RasterOp.SRCINVERT, + SourcePaint = Interop.Gdi32.RasterOp.SRCPAINT, + Whiteness = Interop.Gdi32.RasterOp.WHITENESS, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/DashCap.cs b/src/System.Drawing.Common/src/System/Drawing/DashCap.cs new file mode 100644 index 00000000000..d387fa3a669 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/DashCap.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + /// + /// Specifies the available dash cap styles with which a can end a line. + /// + public enum DashCap + { + Flat = 0, + Round = 2, + Triangle = 3 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Design/CategoryNameCollection.cs b/src/System.Drawing.Common/src/System/Drawing/Design/CategoryNameCollection.cs new file mode 100644 index 00000000000..bf7662bef81 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Design/CategoryNameCollection.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; + +namespace System.Drawing.Design +{ + /// + /// A collection that stores objects. + /// + public sealed class CategoryNameCollection : ReadOnlyCollectionBase + { + /// + /// Initializes a new instance of based on another + /// . + /// + public CategoryNameCollection(CategoryNameCollection value) => InnerList.AddRange(value); + + /// + /// Initializes a new instance of containing any array of + /// objects. + /// + public CategoryNameCollection(string[] value) => InnerList.AddRange(value); + + /// + /// Represents the entry at the specified index of the . + /// + public string this[int index] => ((string)(InnerList[index])!); + + /// + /// Gets a value indicating whether the contains the specified + /// . + /// + public bool Contains(string value) => InnerList.Contains(value); + + /// + /// Copies the values to a one-dimensional instance + /// at the specified index. + /// + public void CopyTo(string[] array, int index) => InnerList.CopyTo(array, index); + + /// + /// Returns the index of a in the . + /// + public int IndexOf(string value) => InnerList.IndexOf(value); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/AdjustableArrowCap.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/AdjustableArrowCap.cs new file mode 100644 index 00000000000..a74e9debc03 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/AdjustableArrowCap.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed partial class AdjustableArrowCap : CustomLineCap + { + internal AdjustableArrowCap(IntPtr nativeCap) : base(nativeCap) { } + + public AdjustableArrowCap(float width, float height) : this(width, height, true) { } + + public AdjustableArrowCap(float width, float height, bool isFilled) + { + IntPtr nativeCap; + int status = Gdip.GdipCreateAdjustableArrowCap(height, width, isFilled, out nativeCap); + Gdip.CheckStatus(status); + SetNativeLineCap(nativeCap); + } + + public float Height + { + get + { + int status = Gdip.GdipGetAdjustableArrowCapHeight(new HandleRef(this, nativeCap), out float height); + Gdip.CheckStatus(status); + return height; + } + set + { + int status = Gdip.GdipSetAdjustableArrowCapHeight(new HandleRef(this, nativeCap), value); + Gdip.CheckStatus(status); + } + } + + public float Width + { + get + { + int status = Gdip.GdipGetAdjustableArrowCapWidth(new HandleRef(this, nativeCap), out float width); + Gdip.CheckStatus(status); + return width; + } + set + { + int status = Gdip.GdipSetAdjustableArrowCapWidth(new HandleRef(this, nativeCap), value); + Gdip.CheckStatus(status); + } + } + + public float MiddleInset + { + get + { + int status = Gdip.GdipGetAdjustableArrowCapMiddleInset(new HandleRef(this, nativeCap), out float middleInset); + Gdip.CheckStatus(status); + return middleInset; + } + set + { + int status = Gdip.GdipSetAdjustableArrowCapMiddleInset(new HandleRef(this, nativeCap), value); + Gdip.CheckStatus(status); + } + } + + public bool Filled + { + get + { + int status = Gdip.GdipGetAdjustableArrowCapFillState(new HandleRef(this, nativeCap), out bool isFilled); + Gdip.CheckStatus(status); + return isFilled; + } + set + { + int status = Gdip.GdipSetAdjustableArrowCapFillState(new HandleRef(this, nativeCap), value); + Gdip.CheckStatus(status); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Blend.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Blend.cs new file mode 100644 index 00000000000..a15571d499b --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Blend.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class Blend + { + public Blend() + { + Factors = new float[1]; + Positions = new float[1]; + } + + public Blend(int count) + { + Factors = new float[count]; + Positions = new float[count]; + } + + public float[] Factors { get; set; } + + public float[] Positions { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/BrushType.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/BrushType.cs new file mode 100644 index 00000000000..2cfd7cff076 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/BrushType.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + internal enum BrushType + { + SolidColor = 0, + HatchFill = 1, + TextureFill = 2, + PathGradient = 3, + LinearGradient = 4 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/ColorBlend.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/ColorBlend.cs new file mode 100644 index 00000000000..1b72c2edabd --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/ColorBlend.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class ColorBlend + { + public ColorBlend() + { + Colors = new Color[1]; + Positions = new float[1]; + } + + public ColorBlend(int count) + { + Colors = new Color[count]; + Positions = new float[count]; + } + + public Color[] Colors { get; set; } + + public float[] Positions { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CombineMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CombineMode.cs new file mode 100644 index 00000000000..f7e3cc7246f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CombineMode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum CombineMode + { + Replace = 0, + Intersect = 1, + Union = 2, + Xor = 3, + Exclude = 4, + Complement = 5 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingMode.cs new file mode 100644 index 00000000000..0406af0ea46 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingMode.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum CompositingMode + { + SourceOver = 0, + SourceCopy = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingQuality.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingQuality.cs new file mode 100644 index 00000000000..6e9818fcc16 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CompositingQuality.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum CompositingQuality + { + Invalid = QualityMode.Invalid, + Default = QualityMode.Default, + HighSpeed = QualityMode.Low, + HighQuality = QualityMode.High, + GammaCorrected, + AssumeLinear + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CoordinateSpace.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CoordinateSpace.cs new file mode 100644 index 00000000000..24ff09c086c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CoordinateSpace.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum CoordinateSpace + { + World = 0, + Page = 1, + Device = 2 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCap.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCap.cs new file mode 100644 index 00000000000..481fdce78c8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCap.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public class CustomLineCap : MarshalByRefObject, ICloneable, IDisposable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + // Handle to native line cap object + internal SafeCustomLineCapHandle nativeCap = null!; + + private bool _disposed; + + // For subclass creation + internal CustomLineCap() { } + + public CustomLineCap(GraphicsPath? fillPath, GraphicsPath? strokePath) : this(fillPath, strokePath, LineCap.Flat) { } + + public CustomLineCap(GraphicsPath? fillPath, GraphicsPath? strokePath, LineCap baseCap) : this(fillPath, strokePath, baseCap, 0) { } + + public CustomLineCap(GraphicsPath? fillPath, GraphicsPath? strokePath, LineCap baseCap, float baseInset) + { + IntPtr nativeLineCap; + int status = Gdip.GdipCreateCustomLineCap( + new HandleRef(fillPath, (fillPath == null) ? IntPtr.Zero : fillPath._nativePath), + new HandleRef(strokePath, (strokePath == null) ? IntPtr.Zero : strokePath._nativePath), + baseCap, baseInset, out nativeLineCap); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + SetNativeLineCap(nativeLineCap); + } + + internal CustomLineCap(IntPtr nativeLineCap) => SetNativeLineCap(nativeLineCap); + + internal static CustomLineCap CreateCustomLineCapObject(IntPtr cap) + { + int status = Gdip.GdipGetCustomLineCapType(cap, out CustomLineCapType capType); + if (status != Gdip.Ok) + { + Gdip.GdipDeleteCustomLineCap(cap); + throw Gdip.StatusException(status); + } + + switch (capType) + { + case CustomLineCapType.Default: + return new CustomLineCap(cap); + + case CustomLineCapType.AdjustableArrowCap: + return new AdjustableArrowCap(cap); + } + + Gdip.GdipDeleteCustomLineCap(cap); + throw Gdip.StatusException(Gdip.NotImplemented); + } + + internal void SetNativeLineCap(IntPtr handle) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException(nameof(handle)); + + nativeCap = new SafeCustomLineCapHandle(handle); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + +#if FINALIZATION_WATCH + if (!disposing && nativeCap != null) + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); +#endif + // propagate the explicit dispose call to the child + if (disposing && nativeCap != null) + { + nativeCap.Dispose(); + } + + _disposed = true; + } + + ~CustomLineCap() => Dispose(false); + + public object Clone() + { + return CoreClone(); + } + + internal virtual object CoreClone() + { + IntPtr clonedCap; + int status = Gdip.GdipCloneCustomLineCap(new HandleRef(this, nativeCap), out clonedCap); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return CreateCustomLineCapObject(clonedCap); + } + + public void SetStrokeCaps(LineCap startCap, LineCap endCap) + { + int status = Gdip.GdipSetCustomLineCapStrokeCaps(new HandleRef(this, nativeCap), startCap, endCap); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void GetStrokeCaps(out LineCap startCap, out LineCap endCap) + { + int status = Gdip.GdipGetCustomLineCapStrokeCaps(new HandleRef(this, nativeCap), out startCap, out endCap); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public LineJoin StrokeJoin + { + get + { + int status = Gdip.GdipGetCustomLineCapStrokeJoin(new HandleRef(this, nativeCap), out LineJoin lineJoin); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return lineJoin; + } + set + { + int status = Gdip.GdipSetCustomLineCapStrokeJoin(new HandleRef(this, nativeCap), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + public LineCap BaseCap + { + get + { + int status = Gdip.GdipGetCustomLineCapBaseCap(new HandleRef(this, nativeCap), out LineCap baseCap); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return baseCap; + } + set + { + int status = Gdip.GdipSetCustomLineCapBaseCap(new HandleRef(this, nativeCap), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + public float BaseInset + { + get + { + int status = Gdip.GdipGetCustomLineCapBaseInset(new HandleRef(this, nativeCap), out float inset); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return inset; + } + set + { + int status = Gdip.GdipSetCustomLineCapBaseInset(new HandleRef(this, nativeCap), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + public float WidthScale + { + get + { + int status = Gdip.GdipGetCustomLineCapWidthScale(new HandleRef(this, nativeCap), out float widthScale); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return widthScale; + } + set + { + int status = Gdip.GdipSetCustomLineCapWidthScale(new HandleRef(this, nativeCap), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCapType.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCapType.cs new file mode 100644 index 00000000000..a5422e172da --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/CustomLineCapType.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + internal enum CustomLineCapType + { + Default = 0, + AdjustableArrowCap = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/DashStyle.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/DashStyle.cs new file mode 100644 index 00000000000..42c3fa3fc6a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/DashStyle.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum DashStyle + { + Solid = 0, + Dash = 1, + Dot = 2, + DashDot = 3, + DashDotDot = 4, + Custom = 5 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FillMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FillMode.cs new file mode 100644 index 00000000000..4b86ef500b8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FillMode.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum FillMode + { + Alternate = 0, + Winding = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FlushIntention.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FlushIntention.cs new file mode 100644 index 00000000000..551f96cf0b9 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/FlushIntention.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + // FlushIntentionFlush merely means that all the pending commands have been passed to + // the hardware, and that the final results will be shown as soon as the hardware finishes + // its rendering. FlushIntentionSync means to wait for the hardware to actually finish its + // rendering before returning - this is important for animation and timing loops. + public enum FlushIntention + { + // Flush all batched rendering operations + Flush = 0, + + // Flush all batched rendering operations and wait for them to complete + Sync = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsContainer.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsContainer.cs new file mode 100644 index 00000000000..f501b3c8b85 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsContainer.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class GraphicsContainer : MarshalByRefObject + { + internal GraphicsContainer(int graphicsContainer) => nativeGraphicsContainer = graphicsContainer; + + internal int nativeGraphicsContainer; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.cs new file mode 100644 index 00000000000..c0918a7b849 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPath.cs @@ -0,0 +1,778 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Internal; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class GraphicsPath : MarshalByRefObject, ICloneable, IDisposable + { + internal IntPtr _nativePath; + + private const float Flatness = (float)2.0 / (float)3.0; + + public GraphicsPath() : this(FillMode.Alternate) { } + + public GraphicsPath(FillMode fillMode) + { + Gdip.CheckStatus(Gdip.GdipCreatePath(unchecked((int)fillMode), out IntPtr nativePath)); + _nativePath = nativePath; + } + + public GraphicsPath(PointF[] pts, byte[] types) : this(pts, types, FillMode.Alternate) { } + + public unsafe GraphicsPath(PointF[] pts, byte[] types, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(pts); + + if (pts.Length != types.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + fixed (PointF* p = pts) + fixed (byte* t = types) + { + Gdip.CheckStatus(Gdip.GdipCreatePath2( + p, t, types.Length, (int)fillMode, out IntPtr nativePath)); + + _nativePath = nativePath; + } + } + + public GraphicsPath(Point[] pts, byte[] types) : this(pts, types, FillMode.Alternate) { } + + public unsafe GraphicsPath(Point[] pts, byte[] types, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(pts); + + if (pts.Length != types.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + fixed (byte* t = types) + fixed (Point* p = pts) + { + Gdip.CheckStatus(Gdip.GdipCreatePath2I( + p, t, types.Length, unchecked((int)fillMode), out IntPtr nativePath)); + + _nativePath = nativePath; + } + } + + public object Clone() + { + Gdip.CheckStatus(Gdip.GdipClonePath(new HandleRef(this, _nativePath), out IntPtr clonedPath)); + + return new GraphicsPath(clonedPath); + } + + private GraphicsPath(IntPtr nativePath) + { + if (nativePath == IntPtr.Zero) + throw new ArgumentNullException(nameof(nativePath)); + + _nativePath = nativePath; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_nativePath != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeletePath(new HandleRef(this, _nativePath)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during Dispose: " + ex.ToString()); + } + finally + { + _nativePath = IntPtr.Zero; + } + } + } + + ~GraphicsPath() => Dispose(false); + + public void Reset() + { + Gdip.CheckStatus(Gdip.GdipResetPath(new HandleRef(this, _nativePath))); + } + + public FillMode FillMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathFillMode(new HandleRef(this, _nativePath), out FillMode fillmode)); + return fillmode; + } + set + { + if (value < FillMode.Alternate || value > FillMode.Winding) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(FillMode)); + + Gdip.CheckStatus(Gdip.GdipSetPathFillMode(new HandleRef(this, _nativePath), value)); + } + } + + private unsafe PathData _GetPathData() + { + int count = PointCount; + + PathData pathData = new PathData() + { + Types = new byte[count], + Points = new PointF[count] + }; + + if (count == 0) + return pathData; + + fixed (byte* t = pathData.Types) + fixed (PointF* p = pathData.Points) + { + GpPathData data = new GpPathData + { + Count = count, + Points = p, + Types = t + }; + + Gdip.CheckStatus(Gdip.GdipGetPathData(new HandleRef(this, _nativePath), &data)); + } + + return pathData; + } + + public PathData PathData => _GetPathData(); + + public void StartFigure() + { + Gdip.CheckStatus(Gdip.GdipStartPathFigure(new HandleRef(this, _nativePath))); + } + + public void CloseFigure() + { + Gdip.CheckStatus(Gdip.GdipClosePathFigure(new HandleRef(this, _nativePath))); + } + + public void CloseAllFigures() + { + Gdip.CheckStatus(Gdip.GdipClosePathFigures(new HandleRef(this, _nativePath))); + } + + public void SetMarkers() + { + Gdip.CheckStatus(Gdip.GdipSetPathMarker(new HandleRef(this, _nativePath))); + } + + public void ClearMarkers() + { + Gdip.CheckStatus(Gdip.GdipClearPathMarkers(new HandleRef(this, _nativePath))); + } + + public void Reverse() + { + Gdip.CheckStatus(Gdip.GdipReversePath(new HandleRef(this, _nativePath))); + } + + public PointF GetLastPoint() + { + Gdip.CheckStatus(Gdip.GdipGetPathLastPoint(new HandleRef(this, _nativePath), out PointF point)); + return point; + } + + public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y), null); + + public bool IsVisible(PointF point) => IsVisible(point, null); + + public bool IsVisible(float x, float y, Graphics? graphics) => IsVisible(new PointF(x, y), graphics); + + public bool IsVisible(PointF pt, Graphics? graphics) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePathPoint( + new HandleRef(this, _nativePath), + pt.X, pt.Y, + new HandleRef(graphics, graphics?.NativeGraphics ?? IntPtr.Zero), + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(int x, int y) => IsVisible(new Point(x, y), null); + + public bool IsVisible(Point point) => IsVisible(point, null); + + public bool IsVisible(int x, int y, Graphics? graphics) => IsVisible(new Point(x, y), graphics); + + public bool IsVisible(Point pt, Graphics? graphics) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePathPointI( + new HandleRef(this, _nativePath), + pt.X, pt.Y, + new HandleRef(graphics, graphics?.NativeGraphics ?? IntPtr.Zero), + out bool isVisible)); + + return isVisible; + } + + public bool IsOutlineVisible(float x, float y, Pen pen) => IsOutlineVisible(new PointF(x, y), pen, null); + + public bool IsOutlineVisible(PointF point, Pen pen) => IsOutlineVisible(point, pen, null); + + public bool IsOutlineVisible(float x, float y, Pen pen, Graphics? graphics) + { + return IsOutlineVisible(new PointF(x, y), pen, graphics); + } + + public bool IsOutlineVisible(PointF pt, Pen pen, Graphics? graphics) + { + ArgumentNullException.ThrowIfNull(pen); + + Gdip.CheckStatus(Gdip.GdipIsOutlineVisiblePathPoint( + new HandleRef(this, _nativePath), + pt.X, pt.Y, + new HandleRef(pen, pen.NativePen), + new HandleRef(graphics, graphics?.NativeGraphics ?? IntPtr.Zero), + out bool isVisible)); + + return isVisible; + } + + public bool IsOutlineVisible(int x, int y, Pen pen) => IsOutlineVisible(new Point(x, y), pen, null); + + public bool IsOutlineVisible(Point point, Pen pen) => IsOutlineVisible(point, pen, null); + + public bool IsOutlineVisible(int x, int y, Pen pen, Graphics? graphics) => IsOutlineVisible(new Point(x, y), pen, graphics); + + public bool IsOutlineVisible(Point pt, Pen pen, Graphics? graphics) + { + ArgumentNullException.ThrowIfNull(pen); + + Gdip.CheckStatus(Gdip.GdipIsOutlineVisiblePathPointI( + new HandleRef(this, _nativePath), + pt.X, pt.Y, + new HandleRef(pen, pen.NativePen), + new HandleRef(graphics, graphics?.NativeGraphics ?? IntPtr.Zero), + out bool isVisible)); + + return isVisible; + } + + public void AddLine(PointF pt1, PointF pt2) => AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + + public void AddLine(float x1, float y1, float x2, float y2) + { + Gdip.CheckStatus(Gdip.GdipAddPathLine(new HandleRef(this, _nativePath), x1, y1, x2, y2)); + } + + public unsafe void AddLines(PointF[] points) + { + ArgumentNullException.ThrowIfNull(points); + + if (points.Length == 0) + throw new ArgumentException(null, nameof(points)); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathLine2(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public void AddLine(Point pt1, Point pt2) => AddLine(pt1.X, pt1.Y, pt2.X, pt2.Y); + + public void AddLine(int x1, int y1, int x2, int y2) + { + Gdip.CheckStatus(Gdip.GdipAddPathLineI(new HandleRef(this, _nativePath), x1, y1, x2, y2)); + } + + public unsafe void AddLines(Point[] points) + { + ArgumentNullException.ThrowIfNull(points); + + if (points.Length == 0) + throw new ArgumentException(null, nameof(points)); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathLine2I(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public void AddArc(RectangleF rect, float startAngle, float sweepAngle) + { + AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + public void AddArc(float x, float y, float width, float height, float startAngle, float sweepAngle) + { + Gdip.CheckStatus(Gdip.GdipAddPathArc( + new HandleRef(this, _nativePath), + x, y, width, height, + startAngle, + sweepAngle)); + } + + public void AddArc(Rectangle rect, float startAngle, float sweepAngle) + { + AddArc(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + public void AddArc(int x, int y, int width, int height, float startAngle, float sweepAngle) + { + Gdip.CheckStatus(Gdip.GdipAddPathArcI( + new HandleRef(this, _nativePath), + x, y, width, height, + startAngle, + sweepAngle)); + } + + public void AddBezier(PointF pt1, PointF pt2, PointF pt3, PointF pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + public void AddBezier(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) + { + Gdip.CheckStatus(Gdip.GdipAddPathBezier( + new HandleRef(this, _nativePath), + x1, y1, x2, y2, x3, y3, x4, y4)); + } + + public unsafe void AddBeziers(PointF[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathBeziers(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public void AddBezier(Point pt1, Point pt2, Point pt3, Point pt4) + { + AddBezier(pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + public void AddBezier(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) + { + Gdip.CheckStatus(Gdip.GdipAddPathBezierI( + new HandleRef(this, _nativePath), + x1, y1, x2, y2, x3, y3, x4, y4)); + } + + public unsafe void AddBeziers(params Point[] points) + { + ArgumentNullException.ThrowIfNull(points); + + if (points.Length == 0) + return; + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathBeziersI(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + /// + /// Add cardinal splines to the path object + /// + public unsafe void AddCurve(PointF[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurve(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public unsafe void AddCurve(PointF[] points, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + if (points.Length == 0) + return; + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurve2(new HandleRef(this, _nativePath), p, points.Length, tension)); + } + } + + public unsafe void AddCurve(PointF[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurve3( + new HandleRef(this, _nativePath), p, points.Length, offset, numberOfSegments, tension)); + } + } + + public unsafe void AddCurve(Point[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurveI(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public unsafe void AddCurve(Point[] points, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurve2I( + new HandleRef(this, _nativePath), p, points.Length, tension)); + } + } + + public unsafe void AddCurve(Point[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathCurve3I( + new HandleRef(this, _nativePath), p, points.Length, offset, numberOfSegments, tension)); + } + } + + public unsafe void AddClosedCurve(PointF[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathClosedCurve( + new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public unsafe void AddClosedCurve(PointF[] points, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathClosedCurve2(new HandleRef(this, _nativePath), p, points.Length, tension)); + } + } + + public unsafe void AddClosedCurve(Point[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathClosedCurveI(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public unsafe void AddClosedCurve(Point[] points, float tension) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathClosedCurve2I(new HandleRef(this, _nativePath), p, points.Length, tension)); + } + } + + public void AddRectangle(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipAddPathRectangle( + new HandleRef(this, _nativePath), + rect.X, rect.Y, rect.Width, rect.Height)); + } + + public unsafe void AddRectangles(RectangleF[] rects) + { + ArgumentNullException.ThrowIfNull(rects); + + if (rects.Length == 0) + throw new ArgumentException(null, nameof(rects)); + + fixed (RectangleF* r = rects) + { + Gdip.CheckStatus(Gdip.GdipAddPathRectangles( + new HandleRef(this, _nativePath), r, rects.Length)); + } + } + + public void AddRectangle(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipAddPathRectangleI( + new HandleRef(this, _nativePath), + rect.X, rect.Y, rect.Width, rect.Height)); + } + + public unsafe void AddRectangles(Rectangle[] rects) + { + ArgumentNullException.ThrowIfNull(rects); + + if (rects.Length == 0) + throw new ArgumentException(null, nameof(rects)); + + fixed (Rectangle* r = rects) + { + Gdip.CheckStatus(Gdip.GdipAddPathRectanglesI( + new HandleRef(this, _nativePath), r, rects.Length)); + } + } + + public void AddEllipse(RectangleF rect) + { + AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); + } + + public void AddEllipse(float x, float y, float width, float height) + { + Gdip.CheckStatus(Gdip.GdipAddPathEllipse(new HandleRef(this, _nativePath), x, y, width, height)); + } + + public void AddEllipse(Rectangle rect) => AddEllipse(rect.X, rect.Y, rect.Width, rect.Height); + + public void AddEllipse(int x, int y, int width, int height) + { + Gdip.CheckStatus(Gdip.GdipAddPathEllipseI(new HandleRef(this, _nativePath), x, y, width, height)); + } + + public void AddPie(Rectangle rect, float startAngle, float sweepAngle) + { + AddPie(rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + public void AddPie(float x, float y, float width, float height, float startAngle, float sweepAngle) + { + Gdip.CheckStatus(Gdip.GdipAddPathPie( + new HandleRef(this, _nativePath), + x, y, width, height, + startAngle, + sweepAngle)); + } + + public void AddPie(int x, int y, int width, int height, float startAngle, float sweepAngle) + { + Gdip.CheckStatus(Gdip.GdipAddPathPieI( + new HandleRef(this, _nativePath), + x, y, width, height, + startAngle, + sweepAngle)); + } + + public unsafe void AddPolygon(PointF[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathPolygon(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + /// + /// Adds a polygon to the current figure. + /// + public unsafe void AddPolygon(Point[] points) + { + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipAddPathPolygonI(new HandleRef(this, _nativePath), p, points.Length)); + } + } + + public void AddPath(GraphicsPath addingPath, bool connect) + { + ArgumentNullException.ThrowIfNull(addingPath); + + Gdip.CheckStatus(Gdip.GdipAddPathPath( + new HandleRef(this, _nativePath), new HandleRef(addingPath, addingPath._nativePath), connect)); + } + + public void AddString(string s, FontFamily family, int style, float emSize, PointF origin, StringFormat? format) + { + AddString(s, family, style, emSize, new RectangleF(origin.X, origin.Y, 0, 0), format); + } + + public void AddString(string s, FontFamily family, int style, float emSize, Point origin, StringFormat? format) + { + AddString(s, family, style, emSize, new Rectangle(origin.X, origin.Y, 0, 0), format); + } + + public void AddString(string s, FontFamily family, int style, float emSize, RectangleF layoutRect, StringFormat? format) + { + ArgumentNullException.ThrowIfNull(family); + + Gdip.CheckStatus(Gdip.GdipAddPathString( + new HandleRef(this, _nativePath), + s, + s.Length, + new HandleRef(family, family?.NativeFamily ?? IntPtr.Zero), + style, + emSize, + ref layoutRect, + new HandleRef(format, format?.nativeFormat ?? IntPtr.Zero))); + } + + public void AddString(string s, FontFamily family, int style, float emSize, Rectangle layoutRect, StringFormat? format) + { + ArgumentNullException.ThrowIfNull(family); + + Gdip.CheckStatus(Gdip.GdipAddPathStringI( + new HandleRef(this, _nativePath), + s, + s.Length, + new HandleRef(family, family?.NativeFamily ?? IntPtr.Zero), + style, + emSize, + ref layoutRect, + new HandleRef(format, format?.nativeFormat ?? IntPtr.Zero))); + } + + public void Transform(Matrix matrix) + { + ArgumentNullException.ThrowIfNull(matrix); + + if (matrix.NativeMatrix == IntPtr.Zero) + return; + + Gdip.CheckStatus(Gdip.GdipTransformPath( + new HandleRef(this, _nativePath), + new HandleRef(matrix, matrix.NativeMatrix))); + } + + public RectangleF GetBounds() => GetBounds(null); + + public RectangleF GetBounds(Matrix? matrix) => GetBounds(matrix, null); + + public RectangleF GetBounds(Matrix? matrix, Pen? pen) + { + Gdip.CheckStatus(Gdip.GdipGetPathWorldBounds( + new HandleRef(this, _nativePath), + out RectangleF bounds, + new HandleRef(matrix, matrix?.NativeMatrix ?? IntPtr.Zero), + new HandleRef(pen, pen?.NativePen ?? IntPtr.Zero))); + + return bounds; + } + + public void Flatten() => Flatten(null); + + public void Flatten(Matrix? matrix) => Flatten(matrix, 0.25f); + + public void Flatten(Matrix? matrix, float flatness) + { + Gdip.CheckStatus(Gdip.GdipFlattenPath( + new HandleRef(this, _nativePath), + new HandleRef(matrix, matrix?.NativeMatrix ?? IntPtr.Zero), + flatness)); + } + + public void Widen(Pen pen) => Widen(pen, null, Flatness); + + public void Widen(Pen pen, Matrix? matrix) => Widen(pen, matrix, Flatness); + + public void Widen(Pen pen, Matrix? matrix, float flatness) + { + ArgumentNullException.ThrowIfNull(pen); + + // GDI+ wrongly returns an out of memory status when there is nothing in the path, so we have to check + // before calling the widen method and do nothing if we dont have anything in the path. + if (PointCount == 0) + return; + + Gdip.CheckStatus(Gdip.GdipWidenPath( + new HandleRef(this, _nativePath), + new HandleRef(pen, pen.NativePen), + new HandleRef(matrix, matrix?.NativeMatrix ?? IntPtr.Zero), + flatness)); + } + + public void Warp(PointF[] destPoints, RectangleF srcRect) => Warp(destPoints, srcRect, null); + + public void Warp(PointF[] destPoints, RectangleF srcRect, Matrix? matrix) => Warp(destPoints, srcRect, matrix, WarpMode.Perspective); + + public void Warp(PointF[] destPoints, RectangleF srcRect, Matrix? matrix, WarpMode warpMode) + { + Warp(destPoints, srcRect, matrix, warpMode, 0.25f); + } + + public unsafe void Warp(PointF[] destPoints, RectangleF srcRect, Matrix? matrix, WarpMode warpMode, float flatness) + { + ArgumentNullException.ThrowIfNull(destPoints); + + fixed (PointF* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipWarpPath( + new HandleRef(this, _nativePath), + new HandleRef(matrix, matrix?.NativeMatrix ?? IntPtr.Zero), + p, + destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + warpMode, + flatness)); + } + } + + public int PointCount + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPointCount(new HandleRef(this, _nativePath), out int count)); + return count; + } + } + + public byte[] PathTypes + { + get + { + byte[] types = new byte[PointCount]; + Gdip.CheckStatus(Gdip.GdipGetPathTypes(new HandleRef(this, _nativePath), types, types.Length)); + return types; + } + } + + public unsafe PointF[] PathPoints + { + get + { + PointF[] points = new PointF[PointCount]; + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipGetPathPoints(new HandleRef(this, _nativePath), p, points.Length)); + } + return points; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs new file mode 100644 index 00000000000..8e83841c4f6 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsPathIterator.cs @@ -0,0 +1,221 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class GraphicsPathIterator : MarshalByRefObject, IDisposable + { + public GraphicsPathIterator(GraphicsPath? path) + { + IntPtr nativeIter = IntPtr.Zero; + int status = Gdip.GdipCreatePathIter(out nativeIter, new HandleRef(path, (path == null) ? IntPtr.Zero : path._nativePath)); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + this.nativeIter = nativeIter; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (nativeIter != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeletePathIter(new HandleRef(this, nativeIter)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during Dispose: " + ex.ToString()); + } + finally + { + nativeIter = IntPtr.Zero; + } + } + } + + ~GraphicsPathIterator() => Dispose(false); + + public int NextSubpath(out int startIndex, out int endIndex, out bool isClosed) + { + int status = Gdip.GdipPathIterNextSubpath(new HandleRef(this, nativeIter), out int resultCount, + out int tempStart, out int tempEnd, out isClosed); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + else + { + startIndex = tempStart; + endIndex = tempEnd; + } + + return resultCount; + } + + public int NextSubpath(GraphicsPath path, out bool isClosed) + { + int status = Gdip.GdipPathIterNextSubpathPath(new HandleRef(this, nativeIter), out int resultCount, + new HandleRef(path, (path == null) ? IntPtr.Zero : path._nativePath), out isClosed); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + + public int NextPathType(out byte pathType, out int startIndex, out int endIndex) + { + int status = Gdip.GdipPathIterNextPathType(new HandleRef(this, nativeIter), out int resultCount, + out pathType, out startIndex, out endIndex); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + + public int NextMarker(out int startIndex, out int endIndex) + { + int status = Gdip.GdipPathIterNextMarker(new HandleRef(this, nativeIter), out int resultCount, + out startIndex, out endIndex); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + + public int NextMarker(GraphicsPath path) + { + int status = Gdip.GdipPathIterNextMarkerPath(new HandleRef(this, nativeIter), out int resultCount, + new HandleRef(path, (path == null) ? IntPtr.Zero : path._nativePath)); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + + public int Count + { + get + { + int status = Gdip.GdipPathIterGetCount(new HandleRef(this, nativeIter), out int resultCount); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + } + + public int SubpathCount + { + get + { + int status = Gdip.GdipPathIterGetSubpathCount(new HandleRef(this, nativeIter), out int resultCount); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return resultCount; + } + } + + public bool HasCurve() + { + int status = Gdip.GdipPathIterHasCurve(new HandleRef(this, nativeIter), out bool hasCurve); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return hasCurve; + } + + public void Rewind() + { + int status = Gdip.GdipPathIterRewind(new HandleRef(this, nativeIter)); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public unsafe int Enumerate(ref PointF[] points, ref byte[] types) + { + if (points.Length != types.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + if (points.Length == 0) + return 0; + + fixed (PointF* p = points) + fixed (byte* t = types) + { + int status = Gdip.GdipPathIterEnumerate( + new HandleRef(this, nativeIter), + out int resultCount, + p, + t, + points.Length); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + return resultCount; + } + } + + public unsafe int CopyData(ref PointF[] points, ref byte[] types, int startIndex, int endIndex) + { + if ((points.Length != types.Length) || (endIndex - startIndex + 1 > points.Length)) + throw Gdip.StatusException(Gdip.InvalidParameter); + + fixed (PointF* p = points) + fixed (byte* t = types) + { + int status = Gdip.GdipPathIterCopyData( + new HandleRef(this, nativeIter), + out int resultCount, + p, + t, + startIndex, + endIndex); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + return resultCount; + } + } + + // handle to native path iterator object + internal IntPtr nativeIter; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsState.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsState.cs new file mode 100644 index 00000000000..2830c42f9eb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/GraphicsState.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class GraphicsState : MarshalByRefObject + { + internal int nativeState; + + internal GraphicsState(int nativeState) => this.nativeState = nativeState; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchBrush.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchBrush.cs new file mode 100644 index 00000000000..c34fe129b8a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchBrush.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Diagnostics; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class HatchBrush : Brush + { + public HatchBrush(HatchStyle hatchstyle, Color foreColor) : this(hatchstyle, foreColor, Color.FromArgb(unchecked((int)0xff000000))) + { + } + + public HatchBrush(HatchStyle hatchstyle, Color foreColor, Color backColor) + { + if (hatchstyle < HatchStyle.Min || hatchstyle > HatchStyle.SolidDiamond) + { + throw new ArgumentException(SR.Format(SR.InvalidEnumArgument, nameof(hatchstyle), hatchstyle, nameof(HatchStyle)), nameof(hatchstyle)); + } + + IntPtr nativeBrush; + int status = Gdip.GdipCreateHatchBrush(unchecked((int)hatchstyle), foreColor.ToArgb(), backColor.ToArgb(), out nativeBrush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(nativeBrush); + } + + internal HatchBrush(IntPtr nativeBrush) + { + Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null."); + SetNativeBrushInternal(nativeBrush); + } + + public override object Clone() + { + IntPtr clonedBrush; + int status = Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out clonedBrush); + Gdip.CheckStatus(status); + + return new HatchBrush(clonedBrush); + } + + public HatchStyle HatchStyle + { + get + { + int hatchStyle; + int status = Gdip.GdipGetHatchStyle(new HandleRef(this, NativeBrush), out hatchStyle); + Gdip.CheckStatus(status); + + return (HatchStyle)hatchStyle; + } + } + + public Color ForegroundColor + { + get + { + int foregroundArgb; + int status = Gdip.GdipGetHatchForegroundColor(new HandleRef(this, NativeBrush), out foregroundArgb); + Gdip.CheckStatus(status); + + return Color.FromArgb(foregroundArgb); + } + } + + public Color BackgroundColor + { + get + { + int backgroundArgb; + int status = Gdip.GdipGetHatchBackgroundColor(new HandleRef(this, NativeBrush), out backgroundArgb); + Gdip.CheckStatus(status); + + return Color.FromArgb(backgroundArgb); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchStyle.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchStyle.cs new file mode 100644 index 00000000000..a5fde08bbc0 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/HatchStyle.cs @@ -0,0 +1,66 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum HatchStyle + { + Horizontal = 0, + Vertical = 1, + ForwardDiagonal = 2, + BackwardDiagonal = 3, + Cross = 4, + DiagonalCross = 5, + Percent05 = 6, + Percent10 = 7, + Percent20 = 8, + Percent25 = 9, + Percent30 = 10, + Percent40 = 11, + Percent50 = 12, + Percent60 = 13, + Percent70 = 14, + Percent75 = 15, + Percent80 = 16, + Percent90 = 17, + LightDownwardDiagonal = 18, + LightUpwardDiagonal = 19, + DarkDownwardDiagonal = 20, + DarkUpwardDiagonal = 21, + WideDownwardDiagonal = 22, + WideUpwardDiagonal = 23, + LightVertical = 24, + LightHorizontal = 25, + NarrowVertical = 26, + NarrowHorizontal = 27, + DarkVertical = 28, + DarkHorizontal = 29, + DashedDownwardDiagonal = 30, + DashedUpwardDiagonal = 31, + DashedHorizontal = 32, + DashedVertical = 33, + SmallConfetti = 34, + LargeConfetti = 35, + ZigZag = 36, + Wave = 37, + DiagonalBrick = 38, + HorizontalBrick = 39, + Weave = 40, + Plaid = 41, + Divot = 42, + DottedGrid = 43, + DottedDiamond = 44, + Shingle = 45, + Trellis = 46, + Sphere = 47, + SmallGrid = 48, + SmallCheckerBoard = 49, + LargeCheckerBoard = 50, + OutlinedDiamond = 51, + SolidDiamond = 52, + + LargeGrid = Cross, + Min = Horizontal, + Max = LargeGrid + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/InterpolationMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/InterpolationMode.cs new file mode 100644 index 00000000000..1bc25cfda14 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/InterpolationMode.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum InterpolationMode + { + Invalid = QualityMode.Invalid, + Default = QualityMode.Default, + Low = QualityMode.Low, + High = QualityMode.High, + Bilinear, + Bicubic, + NearestNeighbor, + HighQualityBilinear, + HighQualityBicubic + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineCap.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineCap.cs new file mode 100644 index 00000000000..9d9a0cec192 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineCap.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum LineCap + { + Flat = 0, + Square = 1, + Round = 2, + Triangle = 3, + NoAnchor = 0x10, // corresponds to flat cap + SquareAnchor = 0x11, // corresponds to square cap + RoundAnchor = 0x12, // corresponds to round cap + DiamondAnchor = 0x13, // corresponds to triangle cap + ArrowAnchor = 0x14, // no correspondence + + Custom = 0xff, // custom cap + AnchorMask = 0xf0 // mask to check for anchor or not. + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineJoin.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineJoin.cs new file mode 100644 index 00000000000..3a5953a6403 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LineJoin.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum LineJoin + { + Miter = 0, + Bevel = 1, + Round = 2, + MiterClipped = 3 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientBrush.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientBrush.cs new file mode 100644 index 00000000000..68b674586aa --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientBrush.cs @@ -0,0 +1,518 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class LinearGradientBrush : Brush + { + private bool _interpolationColorsWasSet; + + public LinearGradientBrush(PointF point1, PointF point2, Color color1, Color color2) + { + Gdip.CheckStatus(Gdip.GdipCreateLineBrush( + ref point1, ref point2, + color1.ToArgb(), color2.ToArgb(), + WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + public LinearGradientBrush(Point point1, Point point2, Color color1, Color color2) + { + Gdip.CheckStatus(Gdip.GdipCreateLineBrushI( + ref point1, ref point2, + color1.ToArgb(), color2.ToArgb(), + WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + public LinearGradientBrush(RectangleF rect, Color color1, Color color2, LinearGradientMode linearGradientMode) + { + if (linearGradientMode < LinearGradientMode.Horizontal || linearGradientMode > LinearGradientMode.BackwardDiagonal) + throw new InvalidEnumArgumentException(nameof(linearGradientMode), unchecked((int)linearGradientMode), typeof(LinearGradientMode)); + + if (rect.Width == 0.0 || rect.Height == 0.0) + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + + Gdip.CheckStatus(Gdip.GdipCreateLineBrushFromRect( + ref rect, + color1.ToArgb(), + color2.ToArgb(), + linearGradientMode, + WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + public LinearGradientBrush(Rectangle rect, Color color1, Color color2, LinearGradientMode linearGradientMode) + { + if (linearGradientMode < LinearGradientMode.Horizontal || linearGradientMode > LinearGradientMode.BackwardDiagonal) + throw new InvalidEnumArgumentException(nameof(linearGradientMode), unchecked((int)linearGradientMode), typeof(LinearGradientMode)); + if (rect.Width == 0 || rect.Height == 0) + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + + Gdip.CheckStatus(Gdip.GdipCreateLineBrushFromRectI( + ref rect, + color1.ToArgb(), + color2.ToArgb(), + linearGradientMode, + WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + public LinearGradientBrush(RectangleF rect, Color color1, Color color2, float angle) : this(rect, color1, color2, angle, false) + { + } + + public LinearGradientBrush(RectangleF rect, Color color1, Color color2, float angle, bool isAngleScaleable) + { + if (rect.Width == 0.0 || rect.Height == 0.0) + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + + Gdip.CheckStatus(Gdip.GdipCreateLineBrushFromRectWithAngle( + ref rect, + color1.ToArgb(), + color2.ToArgb(), + angle, + isAngleScaleable, + (int)WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + public LinearGradientBrush(Rectangle rect, Color color1, Color color2, float angle) : this(rect, color1, color2, angle, false) + { + } + + public LinearGradientBrush(Rectangle rect, Color color1, Color color2, float angle, bool isAngleScaleable) + { + if (rect.Width == 0 || rect.Height == 0) + throw new ArgumentException(SR.Format(SR.GdiplusInvalidRectangle, rect.ToString())); + + Gdip.CheckStatus(Gdip.GdipCreateLineBrushFromRectWithAngleI( + ref rect, + color1.ToArgb(), + color2.ToArgb(), + angle, + isAngleScaleable, + WrapMode.Tile, + out IntPtr nativeBrush)); + + SetNativeBrushInternal(nativeBrush); + } + + internal LinearGradientBrush(IntPtr nativeBrush) + { + Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null."); + SetNativeBrushInternal(nativeBrush); + } + + public override object Clone() + { + Gdip.CheckStatus(Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out IntPtr clonedBrush)); + return new LinearGradientBrush(clonedBrush); + } + + public Color[] LinearColors + { + get + { + int[] colors = new int[] { 0, 0 }; + Gdip.CheckStatus(Gdip.GdipGetLineColors(new HandleRef(this, NativeBrush), colors)); + + return new Color[] + { + Color.FromArgb(colors[0]), + Color.FromArgb(colors[1]) + }; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetLineColors( + new HandleRef(this, NativeBrush), + value[0].ToArgb(), + value[1].ToArgb())); + } + } + + public RectangleF Rectangle + { + get + { + Gdip.CheckStatus(Gdip.GdipGetLineRect(new HandleRef(this, NativeBrush), out RectangleF rect)); + return rect; + } + } + + public bool GammaCorrection + { + get + { + Gdip.CheckStatus(Gdip.GdipGetLineGammaCorrection( + new HandleRef(this, NativeBrush), + out bool useGammaCorrection)); + + return useGammaCorrection; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetLineGammaCorrection(new HandleRef(this, NativeBrush), value)); + } + } + + public Blend? Blend + { + get + { + // Interpolation colors and blends don't work together very well. Getting the Blend when InterpolationColors + // is set puts the Brush into an unusable state afterwards. + // Bail out here to avoid that. + if (_interpolationColorsWasSet) + return null; + + // Figure out the size of blend factor array. + Gdip.CheckStatus(Gdip.GdipGetLineBlendCount(new HandleRef(this, NativeBrush), out int retval)); + + if (retval <= 0) + return null; + + // Allocate a temporary native memory buffer. + int count = retval; + IntPtr factors = IntPtr.Zero; + IntPtr positions = IntPtr.Zero; + + try + { + int size = checked(4 * count); + factors = Marshal.AllocHGlobal(size); + positions = Marshal.AllocHGlobal(size); + + // Retrieve horizontal blend factors. + Gdip.CheckStatus(Gdip.GdipGetLineBlend(new HandleRef(this, NativeBrush), factors, positions, count)); + + // Return the result in a managed array. + var blend = new Blend(count); + + Marshal.Copy(factors, blend.Factors, 0, count); + Marshal.Copy(positions, blend.Positions, 0, count); + + return blend; + } + finally + { + if (factors != IntPtr.Zero) + { + Marshal.FreeHGlobal(factors); + } + if (positions != IntPtr.Zero) + { + Marshal.FreeHGlobal(positions); + } + } + } + set + { + // Do explicit parameter validation here; libgdiplus does not correctly validate the arguments + + // This is the original behavior on Desktop .NET + if (value == null || value.Factors == null) + throw new NullReferenceException(); + + if (value.Positions == null) + throw new ArgumentException(SR.Format(SR.InvalidArgumentValue, "value.Positions", value.Positions), nameof(value)); + + int count = value.Factors.Length; + + if (count == 0 || value.Positions.Length == 0) + throw new ArgumentException(SR.BlendObjectMustHaveTwoElements); + if (count >= 2 && count != value.Positions.Length) + throw new ArgumentOutOfRangeException(nameof(value)); + if (count >= 2 && value.Positions[0] != 0.0F) + throw new ArgumentException(SR.BlendObjectFirstElementInvalid); + if (count >= 2 && value.Positions[count - 1] != 1.0F) + throw new ArgumentException(SR.BlendObjectLastElementInvalid); + + // Allocate temporary native memory buffer and copy input blend factors into it. + IntPtr factors = IntPtr.Zero; + IntPtr positions = IntPtr.Zero; + + try + { + int size = checked(4 * count); + factors = Marshal.AllocHGlobal(size); + positions = Marshal.AllocHGlobal(size); + + Marshal.Copy(value.Factors, 0, factors, count); + Marshal.Copy(value.Positions, 0, positions, count); + + // Set blend factors. + Gdip.CheckStatus(Gdip.GdipSetLineBlend( + new HandleRef(this, NativeBrush), + factors, + positions, + count)); + } + finally + { + if (factors != IntPtr.Zero) + { + Marshal.FreeHGlobal(factors); + } + if (positions != IntPtr.Zero) + { + Marshal.FreeHGlobal(positions); + } + } + } + } + + public void SetSigmaBellShape(float focus) => SetSigmaBellShape(focus, (float)1.0); + + public void SetSigmaBellShape(float focus, float scale) + { + if (focus < 0 || focus > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(focus)); + if (scale < 0 || scale > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(scale)); + + Gdip.CheckStatus(Gdip.GdipSetLineSigmaBlend(new HandleRef(this, NativeBrush), focus, scale)); + } + + public void SetBlendTriangularShape(float focus) => SetBlendTriangularShape(focus, (float)1.0); + + public void SetBlendTriangularShape(float focus, float scale) + { + if (focus < 0 || focus > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(focus)); + if (scale < 0 || scale > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(scale)); + + Gdip.CheckStatus(Gdip.GdipSetLineLinearBlend(new HandleRef(this, NativeBrush), focus, scale)); + + // Setting a triangular shape overrides the explicitly set interpolation colors. libgdiplus correctly clears + // the interpolation colors (https://github.com/mono/libgdiplus/blob/master/src/lineargradientbrush.c#L959) but + // returns WrongState instead of ArgumentException (https://github.com/mono/libgdiplus/blob/master/src/lineargradientbrush.c#L814) + // when calling GdipGetLinePresetBlend, so it is important we set this to false. This way, we are sure get_InterpolationColors + // will return an ArgumentException. + _interpolationColorsWasSet = false; + } + + public ColorBlend InterpolationColors + { + get + { + if (!_interpolationColorsWasSet) + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsColorBlendNotSet, string.Empty)); + + // Figure out the size of blend factor array. + Gdip.CheckStatus(Gdip.GdipGetLinePresetBlendCount(new HandleRef(this, NativeBrush), out int retval)); + + // Allocate temporary native memory buffer. + int count = retval; + + IntPtr colors = IntPtr.Zero; + IntPtr positions = IntPtr.Zero; + + try + { + int size = checked(4 * count); + colors = Marshal.AllocHGlobal(size); + positions = Marshal.AllocHGlobal(size); + + // Retrieve horizontal blend factors. + Gdip.CheckStatus(Gdip.GdipGetLinePresetBlend(new HandleRef(this, NativeBrush), colors, positions, count)); + + // Return the result in a managed array. + var blend = new ColorBlend(count); + + int[] argb = new int[count]; + Marshal.Copy(colors, argb, 0, count); + Marshal.Copy(positions, blend.Positions, 0, count); + + // Copy ARGB values into Color array of ColorBlend. + blend.Colors = new Color[argb.Length]; + + for (int i = 0; i < argb.Length; i++) + { + blend.Colors[i] = Color.FromArgb(argb[i]); + } + + return blend; + } + finally + { + if (colors != IntPtr.Zero) + { + Marshal.FreeHGlobal(colors); + } + if (positions != IntPtr.Zero) + { + Marshal.FreeHGlobal(positions); + } + } + } + set + { + _interpolationColorsWasSet = true; + + if (value == null) + { + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsInvalidColorBlendObject, string.Empty)); + } + else if (value.Colors.Length < 2) + { + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsInvalidColorBlendObject, + SR.InterpolationColorsLength)); + } + else if (value.Colors.Length != value.Positions.Length) + { + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsInvalidColorBlendObject, + SR.InterpolationColorsLengthsDiffer)); + } + else if (value.Positions[0] != 0.0f) + { + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsInvalidColorBlendObject, + SR.InterpolationColorsInvalidStartPosition)); + } + else if (value.Positions[value.Positions.Length - 1] != 1.0f) + { + throw new ArgumentException(SR.Format(SR.InterpolationColorsCommon, + SR.InterpolationColorsInvalidColorBlendObject, + SR.InterpolationColorsInvalidEndPosition)); + } + + + // Allocate a temporary native memory buffer and copy input blend factors into it. + int count = value.Colors.Length; + IntPtr colors = IntPtr.Zero; + IntPtr positions = IntPtr.Zero; + + try + { + int size = checked(4 * count); + colors = Marshal.AllocHGlobal(size); + positions = Marshal.AllocHGlobal(size); + + int[] argbs = new int[count]; + for (int i = 0; i < count; i++) + { + argbs[i] = value.Colors[i].ToArgb(); + } + + Marshal.Copy(argbs, 0, colors, count); + Marshal.Copy(value.Positions, 0, positions, count); + + // Set blend factors. + Gdip.CheckStatus(Gdip.GdipSetLinePresetBlend(new HandleRef(this, NativeBrush), colors, positions, count)); + } + finally + { + if (colors != IntPtr.Zero) + { + Marshal.FreeHGlobal(colors); + } + if (positions != IntPtr.Zero) + { + Marshal.FreeHGlobal(positions); + } + } + } + } + + public WrapMode WrapMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetLineWrapMode(new HandleRef(this, NativeBrush), out int mode)); + return (WrapMode)mode; + } + set + { + if (value < WrapMode.Tile || value > WrapMode.Clamp) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(WrapMode)); + + Gdip.CheckStatus(Gdip.GdipSetLineWrapMode(new HandleRef(this, NativeBrush), unchecked((int)value))); + } + } + + public Matrix Transform + { + get + { + var matrix = new Matrix(); + Gdip.CheckStatus(Gdip.GdipGetLineTransform(new HandleRef(this, NativeBrush), new HandleRef(matrix, matrix.NativeMatrix))); + return matrix; + } + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + Gdip.CheckStatus(Gdip.GdipSetLineTransform(new HandleRef(this, NativeBrush), new HandleRef(value, value.NativeMatrix))); + } + } + + public void ResetTransform() + { + Gdip.CheckStatus(Gdip.GdipResetLineTransform(new HandleRef(this, NativeBrush))); + } + + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws + // with the libgdiplus backend. Simulate a nop for compatibility with GDI+. + if (matrix.NativeMatrix == IntPtr.Zero) + return; + + Gdip.CheckStatus(Gdip.GdipMultiplyLineTransform( + new HandleRef(this, NativeBrush), + new HandleRef(matrix, matrix.NativeMatrix), + order)); + } + + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipTranslateLineTransform( + new HandleRef(this, NativeBrush), dx, dy, order)); + } + + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipScaleLineTransform( + new HandleRef(this, NativeBrush), sx, sy, order)); + } + + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + public void RotateTransform(float angle, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipRotateLineTransform( + new HandleRef(this, NativeBrush), angle, order)); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientMode.cs new file mode 100644 index 00000000000..d51c5c8a690 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/LinearGradientMode.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public enum LinearGradientMode + { + Horizontal = 0, + Vertical = 1, + ForwardDiagonal = 2, + BackwardDiagonal = 3 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs new file mode 100644 index 00000000000..0a90b2da9ea --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/Matrix.cs @@ -0,0 +1,336 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class Matrix : MarshalByRefObject, IDisposable + { + internal IntPtr NativeMatrix { get; private set; } + + public Matrix() + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix(out IntPtr nativeMatrix)); + NativeMatrix = nativeMatrix; + } + + public Matrix(float m11, float m12, float m21, float m22, float dx, float dy) + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix2(m11, m12, m21, m22, dx, dy, out IntPtr nativeMatrix)); + NativeMatrix = nativeMatrix; + } + + /// + /// Construct a utilizing the given . + /// + /// Matrix data to construct from. + public Matrix(Matrix3x2 matrix) : this(CreateNativeHandle(matrix)) + { + } + + private Matrix(IntPtr nativeMatrix) + { + NativeMatrix = nativeMatrix; + } + + internal static IntPtr CreateNativeHandle(Matrix3x2 matrix) + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix2( + matrix.M11, + matrix.M12, + matrix.M21, + matrix.M22, + matrix.M31, + matrix.M32, + out IntPtr nativeMatrix)); + + return nativeMatrix; + } + + public unsafe Matrix(RectangleF rect, PointF[] plgpts) + { + ArgumentNullException.ThrowIfNull(plgpts); + + if (plgpts.Length != 3) + throw Gdip.StatusException(Gdip.InvalidParameter); + + fixed (PointF* p = plgpts) + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix3(ref rect, p, out IntPtr nativeMatrix)); + NativeMatrix = nativeMatrix; + } + } + + public unsafe Matrix(Rectangle rect, Point[] plgpts) + { + ArgumentNullException.ThrowIfNull(plgpts); + + if (plgpts.Length != 3) + throw Gdip.StatusException(Gdip.InvalidParameter); + + fixed (Point* p = plgpts) + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix3I(ref rect, p, out IntPtr nativeMatrix)); + NativeMatrix = nativeMatrix; + } + } + + public void Dispose() + { + DisposeInternal(); + GC.SuppressFinalize(this); + } + + private void DisposeInternal() + { + if (NativeMatrix != IntPtr.Zero) + { + if (Gdip.Initialized) + { + Gdip.GdipDeleteMatrix(new HandleRef(this, NativeMatrix)); + } + NativeMatrix = IntPtr.Zero; + } + } + + ~Matrix() => DisposeInternal(); + + public Matrix Clone() + { + Gdip.CheckStatus(Gdip.GdipCloneMatrix(new HandleRef(this, NativeMatrix), out IntPtr clonedMatrix)); + return new Matrix(clonedMatrix); + } + + public float[] Elements + { + get + { + float[] elements = new float[6]; + GetElements(elements); + return elements; + } + } + + /// + /// Gets/sets the elements for the matrix. + /// + public unsafe Matrix3x2 MatrixElements + { + get + { + Matrix3x2 matrix = default; + Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(this, NativeMatrix), (float*)&matrix)); + return matrix; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetMatrixElements( + new HandleRef(this, NativeMatrix), + value.M11, + value.M12, + value.M21, + value.M22, + value.M31, + value.M32)); + } + } + + internal unsafe void GetElements(Span elements) + { + Debug.Assert(elements.Length >= 6); + + fixed (float* m = elements) + { + Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(this, NativeMatrix), m)); + } + } + + public unsafe float OffsetX => Offset.X; + + public unsafe float OffsetY => Offset.Y; + + internal unsafe PointF Offset + { + get + { + Span elements = stackalloc float[6]; + GetElements(elements); + return new PointF(elements[4], elements[5]); + } + } + + public void Reset() + { + Gdip.CheckStatus(Gdip.GdipSetMatrixElements( + new HandleRef(this, NativeMatrix), + 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f)); + } + + public void Multiply(Matrix matrix) => Multiply(matrix, MatrixOrder.Prepend); + + public void Multiply(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + if (matrix.NativeMatrix == NativeMatrix) + throw new InvalidOperationException(SR.GdiplusObjectBusy); + + Gdip.CheckStatus(Gdip.GdipMultiplyMatrix( + new HandleRef(this, NativeMatrix), + new HandleRef(matrix, matrix.NativeMatrix), + order)); + } + + public void Translate(float offsetX, float offsetY) => Translate(offsetX, offsetY, MatrixOrder.Prepend); + + public void Translate(float offsetX, float offsetY, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipTranslateMatrix( + new HandleRef(this, NativeMatrix), + offsetX, offsetY, order)); + } + + public void Scale(float scaleX, float scaleY) => Scale(scaleX, scaleY, MatrixOrder.Prepend); + + public void Scale(float scaleX, float scaleY, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipScaleMatrix(new HandleRef(this, NativeMatrix), scaleX, scaleY, order)); + } + + public void Rotate(float angle) => Rotate(angle, MatrixOrder.Prepend); + + public void Rotate(float angle, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipRotateMatrix(new HandleRef(this, NativeMatrix), angle, order)); + } + + public void RotateAt(float angle, PointF point) => RotateAt(angle, point, MatrixOrder.Prepend); + public void RotateAt(float angle, PointF point, MatrixOrder order) + { + int status; + if (order == MatrixOrder.Prepend) + { + status = Gdip.GdipTranslateMatrix(new HandleRef(this, NativeMatrix), point.X, point.Y, order); + status |= Gdip.GdipRotateMatrix(new HandleRef(this, NativeMatrix), angle, order); + status |= Gdip.GdipTranslateMatrix(new HandleRef(this, NativeMatrix), -point.X, -point.Y, order); + } + else + { + status = Gdip.GdipTranslateMatrix(new HandleRef(this, NativeMatrix), -point.X, -point.Y, order); + status |= Gdip.GdipRotateMatrix(new HandleRef(this, NativeMatrix), angle, order); + status |= Gdip.GdipTranslateMatrix(new HandleRef(this, NativeMatrix), point.X, point.Y, order); + } + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void Shear(float shearX, float shearY) + { + Gdip.CheckStatus(Gdip.GdipShearMatrix(new HandleRef(this, NativeMatrix), shearX, shearY, MatrixOrder.Prepend)); + } + + public void Shear(float shearX, float shearY, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipShearMatrix(new HandleRef(this, NativeMatrix), shearX, shearY, order)); + } + + public void Invert() + { + Gdip.CheckStatus(Gdip.GdipInvertMatrix(new HandleRef(this, NativeMatrix))); + } + + public unsafe void TransformPoints(PointF[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (PointF* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformMatrixPoints( + new HandleRef(this, NativeMatrix), + p, + pts.Length)); + } + } + + public unsafe void TransformPoints(Point[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (Point* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformMatrixPointsI( + new HandleRef(this, NativeMatrix), + p, + pts.Length)); + } + } + + public unsafe void TransformVectors(PointF[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (PointF* p = pts) + { + Gdip.CheckStatus(Gdip.GdipVectorTransformMatrixPoints( + new HandleRef(this, NativeMatrix), + p, + pts.Length)); + } + } + + public void VectorTransformPoints(Point[] pts) => TransformVectors(pts); + + public unsafe void TransformVectors(Point[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (Point* p = pts) + { + Gdip.CheckStatus(Gdip.GdipVectorTransformMatrixPointsI( + new HandleRef(this, NativeMatrix), + p, + pts.Length)); + } + } + + public bool IsInvertible + { + get + { + Gdip.CheckStatus(Gdip.GdipIsMatrixInvertible(new HandleRef(this, NativeMatrix), out int isInvertible)); + return isInvertible != 0; + } + } + + public bool IsIdentity + { + get + { + Gdip.CheckStatus(Gdip.GdipIsMatrixIdentity(new HandleRef(this, NativeMatrix), out int isIdentity)); + return isIdentity != 0; + } + } + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (!(obj is Matrix matrix2)) + return false; + + Gdip.CheckStatus(Gdip.GdipIsMatrixEqual( + new HandleRef(this, NativeMatrix), + new HandleRef(matrix2, matrix2.NativeMatrix), + out int isEqual)); + + return isEqual != 0; + } + + public override int GetHashCode() => base.GetHashCode(); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/MatrixOrder.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/MatrixOrder.cs new file mode 100644 index 00000000000..8910e0e88c3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/MatrixOrder.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum MatrixOrder + { + Prepend = 0, + Append = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathData.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathData.cs new file mode 100644 index 00000000000..0fbe7ed638c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathData.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class PathData + { + public PathData() { } + + public PointF[]? Points { get; set; } + + public byte[]? Types { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathGradientBrush.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathGradientBrush.cs new file mode 100644 index 00000000000..8b548b1ae43 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathGradientBrush.cs @@ -0,0 +1,408 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.ComponentModel; +using System.Drawing.Internal; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + public sealed class PathGradientBrush : Brush + { + public PathGradientBrush(PointF[] points) : this(points, WrapMode.Clamp) { } + + public unsafe PathGradientBrush(PointF[] points, WrapMode wrapMode) + { + ArgumentNullException.ThrowIfNull(points); + + if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp) + throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode)); + + // GdipCreatePathGradient returns InsufficientBuffer for less than 3 points, which we turn into + // OutOfMemoryException(). We used to copy nothing into an empty native buffer for zero points, + // which gives a valid pointer. Fixing an empty array gives a null pointer, which causes an + // InvalidParameter result, which we turn into an ArgumentException. Matching the old throw. + if (points.Length == 0) + throw new OutOfMemoryException(); + + fixed (PointF* p = points) + { + Gdip.CheckStatus(Gdip.GdipCreatePathGradient( + p, points.Length, wrapMode, out IntPtr nativeBrush)); + SetNativeBrushInternal(nativeBrush); + } + } + + public PathGradientBrush(Point[] points) : this(points, WrapMode.Clamp) { } + + public unsafe PathGradientBrush(Point[] points, WrapMode wrapMode) + { + ArgumentNullException.ThrowIfNull(points); + + if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp) + throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode)); + + // GdipCreatePathGradient returns InsufficientBuffer for less than 3 points, which we turn into + // OutOfMemoryException(). We used to copy nothing into an empty native buffer for zero points, + // which gives a valid pointer. Fixing an empty array gives a null pointer, which causes an + // InvalidParameter result, which we turn into an ArgumentException. Matching the old throw. + if (points.Length == 0) + throw new OutOfMemoryException(); + + fixed (Point* p = points) + { + Gdip.CheckStatus(Gdip.GdipCreatePathGradientI( + p, points.Length, wrapMode, out IntPtr nativeBrush)); + SetNativeBrushInternal(nativeBrush); + } + } + + public PathGradientBrush(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCreatePathGradientFromPath(new HandleRef(path, path._nativePath), out IntPtr nativeBrush)); + SetNativeBrushInternal(nativeBrush); + } + + internal PathGradientBrush(IntPtr nativeBrush) + { + Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null."); + SetNativeBrushInternal(nativeBrush); + } + + public override object Clone() + { + Gdip.CheckStatus(Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out IntPtr clonedBrush)); + return new PathGradientBrush(clonedBrush); + } + + public Color CenterColor + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathGradientCenterColor(new HandleRef(this, NativeBrush), out int argb)); + return Color.FromArgb(argb); + } + set + { + Gdip.CheckStatus(Gdip.GdipSetPathGradientCenterColor(new HandleRef(this, NativeBrush), value.ToArgb())); + } + } + + public Color[] SurroundColors + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathGradientSurroundColorCount( + new HandleRef(this, NativeBrush), + out int count)); + + int[] argbs = new int[count]; + + Gdip.CheckStatus(Gdip.GdipGetPathGradientSurroundColorsWithCount( + new HandleRef(this, NativeBrush), + argbs, + ref count)); + + Color[] colors = new Color[count]; + for (int i = 0; i < count; i++) + colors[i] = Color.FromArgb(argbs[i]); + + return colors; + } + set + { + int count = value.Length; + int[] argbs = new int[count]; + for (int i = 0; i < value.Length; i++) + argbs[i] = value[i].ToArgb(); + + Gdip.CheckStatus(Gdip.GdipSetPathGradientSurroundColorsWithCount( + new HandleRef(this, NativeBrush), + argbs, + ref count)); + } + } + + public PointF CenterPoint + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathGradientCenterPoint(new HandleRef(this, NativeBrush), out PointF point)); + return point; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetPathGradientCenterPoint(new HandleRef(this, NativeBrush), ref value)); + } + } + + public RectangleF Rectangle + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathGradientRect(new HandleRef(this, NativeBrush), out RectangleF rect)); + return rect; + } + } + + public Blend Blend + { + get + { + // Figure out the size of blend factor array + Gdip.CheckStatus(Gdip.GdipGetPathGradientBlendCount(new HandleRef(this, NativeBrush), out int retval)); + + // Allocate temporary native memory buffer + + int count = retval; + + var factors = new float[count]; + var positions = new float[count]; + + // Retrieve horizontal blend factors + + Gdip.CheckStatus(Gdip.GdipGetPathGradientBlend(new HandleRef(this, NativeBrush), factors, positions, count)); + + // Return the result in a managed array + + Blend blend = new Blend(count) + { + Factors = factors, + Positions = positions + }; + + return blend; + } + set + { + // This is the behavior on Desktop + if (value == null || value.Factors == null) + throw new NullReferenceException(); + + if (value.Positions == null) + throw new ArgumentException(SR.Format(SR.InvalidArgumentValue, "value.Positions", value.Positions), nameof(value)); + + int count = value.Factors.Length; + + // Explicit argument validation, because libgdiplus does not correctly validate all parameters. + if (count == 0 || value.Positions.Length == 0) + throw new ArgumentException(SR.BlendObjectMustHaveTwoElements); + if (count >= 2 && count != value.Positions.Length) + throw new ArgumentOutOfRangeException(nameof(value)); + if (count >= 2 && value.Positions[0] != 0.0F) + throw new ArgumentException(SR.BlendObjectFirstElementInvalid); + if (count >= 2 && value.Positions[count - 1] != 1.0F) + throw new ArgumentException(SR.BlendObjectLastElementInvalid); + + // Allocate temporary native memory buffer + // and copy input blend factors into it. + IntPtr factors = IntPtr.Zero; + IntPtr positions = IntPtr.Zero; + + try + { + int size = checked(4 * count); + factors = Marshal.AllocHGlobal(size); + positions = Marshal.AllocHGlobal(size); + + Marshal.Copy(value.Factors, 0, factors, count); + Marshal.Copy(value.Positions, 0, positions, count); + + // Set blend factors + + Gdip.CheckStatus(Gdip.GdipSetPathGradientBlend(new HandleRef(this, NativeBrush), factors, positions, count)); + } + finally + { + if (factors != IntPtr.Zero) + { + Marshal.FreeHGlobal(factors); + } + if (positions != IntPtr.Zero) + { + Marshal.FreeHGlobal(positions); + } + } + } + } + + public void SetSigmaBellShape(float focus) => SetSigmaBellShape(focus, (float)1.0); + + public void SetSigmaBellShape(float focus, float scale) + { + if (focus < 0 || focus > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(focus)); + if (scale < 0 || scale > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(scale)); + + Gdip.CheckStatus(Gdip.GdipSetPathGradientSigmaBlend(new HandleRef(this, NativeBrush), focus, scale)); + } + + public void SetBlendTriangularShape(float focus) => SetBlendTriangularShape(focus, (float)1.0); + + public void SetBlendTriangularShape(float focus, float scale) + { + if (focus < 0 || focus > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(focus)); + if (scale < 0 || scale > 1) + throw new ArgumentException(SR.GdiplusInvalidParameter, nameof(scale)); + + Gdip.CheckStatus(Gdip.GdipSetPathGradientLinearBlend(new HandleRef(this, NativeBrush), focus, scale)); + } + + public ColorBlend InterpolationColors + { + get + { + // Figure out the size of blend factor array + Gdip.CheckStatus(Gdip.GdipGetPathGradientPresetBlendCount(new HandleRef(this, NativeBrush), out int count)); + + // If count is 0, then there is nothing to marshal. + // In this case, we'll return an empty ColorBlend... + if (count == 0) + return new ColorBlend(); + + int[] colors = new int[count]; + float[] positions = new float[count]; + + ColorBlend blend = new ColorBlend(count); + + // status would fail if we ask points or types with a < 2 count + if (count < 2) + return blend; + + // Retrieve horizontal blend factors + Gdip.CheckStatus(Gdip.GdipGetPathGradientPresetBlend(new HandleRef(this, NativeBrush), colors, positions, count)); + + // Return the result in a managed array + + blend.Positions = positions; + + // copy ARGB values into Color array of ColorBlend + blend.Colors = new Color[count]; + + for (int i = 0; i < count; i++) + { + blend.Colors[i] = Color.FromArgb(colors[i]); + } + + return blend; + } + set + { + int count = value.Colors.Length; + + if (value.Positions == null) + throw new ArgumentException(SR.Format(SR.InvalidArgumentValue, "value.Positions", value.Positions), nameof(value)); + if (value.Colors.Length != value.Positions.Length) + throw new ArgumentOutOfRangeException(nameof(value)); + + float[] positions = value.Positions; + int[] argbs = new int[count]; + for (int i = 0; i < count; i++) + { + argbs[i] = value.Colors[i].ToArgb(); + } + + // Set blend factors + Gdip.CheckStatus(Gdip.GdipSetPathGradientPresetBlend(new HandleRef(this, NativeBrush), argbs, positions, count)); + } + } + + public Matrix Transform + { + get + { + Matrix matrix = new Matrix(); + Gdip.CheckStatus(Gdip.GdipGetPathGradientTransform(new HandleRef(this, NativeBrush), new HandleRef(matrix, matrix.NativeMatrix))); + return matrix; + } + set + { + if (value == null) + throw new ArgumentNullException(nameof(value)); + + Gdip.CheckStatus(Gdip.GdipSetPathGradientTransform(new HandleRef(this, NativeBrush), new HandleRef(value, value.NativeMatrix))); + } + } + + public void ResetTransform() + { + Gdip.CheckStatus(Gdip.GdipResetPathGradientTransform(new HandleRef(this, NativeBrush))); + } + + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws + // with the libgdiplus backend. Simulate a nop for compatibility with GDI+. + if (matrix.NativeMatrix == IntPtr.Zero) + return; + + Gdip.CheckStatus(Gdip.GdipMultiplyPathGradientTransform( + new HandleRef(this, NativeBrush), + new HandleRef(matrix, matrix.NativeMatrix), + order)); + } + + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipTranslatePathGradientTransform(new HandleRef(this, NativeBrush), dx, dy, order)); + } + + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipScalePathGradientTransform(new HandleRef(this, NativeBrush), sx, sy, order)); + } + + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + public void RotateTransform(float angle, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipRotatePathGradientTransform(new HandleRef(this, NativeBrush), angle, order)); + } + + public PointF FocusScales + { + get + { + float[] scaleX = new float[] { 0.0f }; + float[] scaleY = new float[] { 0.0f }; + + Gdip.CheckStatus(Gdip.GdipGetPathGradientFocusScales(new HandleRef(this, NativeBrush), scaleX, scaleY)); + return new PointF(scaleX[0], scaleY[0]); + } + set + { + Gdip.CheckStatus(Gdip.GdipSetPathGradientFocusScales(new HandleRef(this, NativeBrush), value.X, value.Y)); + } + } + + public WrapMode WrapMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPathGradientWrapMode(new HandleRef(this, NativeBrush), out int mode)); + return (WrapMode)mode; + } + set + { + if (value < WrapMode.Tile || value > WrapMode.Clamp) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(WrapMode)); + + Gdip.CheckStatus(Gdip.GdipSetPathGradientWrapMode(new HandleRef(this, NativeBrush), unchecked((int)value))); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathPointType.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathPointType.cs new file mode 100644 index 00000000000..a2b0945477c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PathPointType.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum PathPointType + { + Start = 0, // move + Line = 1, // line + Bezier = 3, // default Beizer (= cubic Bezier) + PathTypeMask = 0x07, // type mask (lowest 3 bits). + DashMode = 0x10, // currently in dash mode. + PathMarker = 0x20, // a marker for the path. + CloseSubpath = 0x80, // closed flag + + // Path types used for advanced path. + Bezier3 = 3, // cubic Bezier + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenAlignment.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenAlignment.cs new file mode 100644 index 00000000000..85ae097e88d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenAlignment.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum PenAlignment + { + Center = 0, + Inset = 1, + Outset = 2, + Left = 3, + Right = 4 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenType.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenType.cs new file mode 100644 index 00000000000..dcc22d87b73 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PenType.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum PenType + { + SolidColor = BrushType.SolidColor, + HatchFill = BrushType.HatchFill, + TextureFill = BrushType.TextureFill, + PathGradient = BrushType.PathGradient, + LinearGradient = BrushType.LinearGradient, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PixelOffsetMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PixelOffsetMode.cs new file mode 100644 index 00000000000..11dc9c598d4 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/PixelOffsetMode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum PixelOffsetMode + { + Invalid = QualityMode.Invalid, + Default = QualityMode.Default, + HighSpeed = QualityMode.Low, + HighQuality = QualityMode.High, + None, + Half // offset by -0.5, -0.5 for fast anti-alias perf + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/QualityMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/QualityMode.cs new file mode 100644 index 00000000000..565494343e9 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/QualityMode.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum QualityMode + { + Invalid = -1, + Default = 0, + Low = 1, + High = 2 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/RegionData.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/RegionData.cs new file mode 100644 index 00000000000..ea6eb21b8ae --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/RegionData.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public sealed class RegionData + { + internal RegionData(byte[] data) => Data = data; + + public byte[] Data { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs new file mode 100644 index 00000000000..410f4478913 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SafeCustomLineCapHandle.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Drawing2D +{ + internal sealed class SafeCustomLineCapHandle : SafeHandle + { + public SafeCustomLineCapHandle() : base(IntPtr.Zero, true) + { + } + + // Create a SafeHandle, informing the base class + // that this SafeHandle instance "owns" the handle, + // and therefore SafeHandle should call + // our ReleaseHandle method when the SafeHandle + // is no longer in use. + internal SafeCustomLineCapHandle(IntPtr h) : base(IntPtr.Zero, true) + { + SetHandle(h); + } + + protected override bool ReleaseHandle() + { + int status = Gdip.Ok; + if (!IsInvalid) + { + try + { + status = !Gdip.Initialized ? Gdip.Ok : + Gdip.GdipDeleteCustomLineCap(new HandleRef(this, handle)); + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during ReleaseHandle: " + ex.ToString()); + } + finally + { + handle = IntPtr.Zero; + } + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); + } + return status == Gdip.Ok; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + public static implicit operator IntPtr(SafeCustomLineCapHandle handle) => handle?.handle ?? IntPtr.Zero; + + public static explicit operator SafeCustomLineCapHandle(IntPtr handle) => new SafeCustomLineCapHandle(handle); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SmoothingMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SmoothingMode.cs new file mode 100644 index 00000000000..64a162fb760 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/SmoothingMode.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum SmoothingMode + { + Invalid = QualityMode.Invalid, + Default = QualityMode.Default, + HighSpeed = QualityMode.Low, + HighQuality = QualityMode.High, + None, + AntiAlias + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WarpMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WarpMode.cs new file mode 100644 index 00000000000..75c4764f686 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WarpMode.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum WarpMode + { + Perspective = 0, + Bilinear = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WrapMode.cs b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WrapMode.cs new file mode 100644 index 00000000000..22799810e6b --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Drawing2D/WrapMode.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Drawing2D +{ + public enum WrapMode + { + Tile = 0, + TileFlipX = 1, + TileFlipY = 2, + TileFlipXY = 3, + Clamp = 4 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/DrawingCom.ComWrappers.cs b/src/System.Drawing.Common/src/System/Drawing/DrawingCom.ComWrappers.cs new file mode 100644 index 00000000000..11f84642eb7 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/DrawingCom.ComWrappers.cs @@ -0,0 +1,325 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// The ComWrappers implementation for System.Drawing.Common's COM interop usages. + /// + /// Supports IStream and IPicture COM interfaces. + /// + internal unsafe partial class DrawingCom : ComWrappers + { + private const int S_OK = (int)Interop.HRESULT.S_OK; + private static readonly Guid IID_IStream = new Guid(0x0000000C, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); + + private static readonly ComInterfaceEntry* s_wrapperEntry = InitializeComInterfaceEntry(); + + internal static DrawingCom Instance { get; } = new DrawingCom(); + + private DrawingCom() { } + + private static ComInterfaceEntry* InitializeComInterfaceEntry() + { + GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease); + + IntPtr iStreamVtbl = IStreamVtbl.Create(fpQueryInterface, fpAddRef, fpRelease); + + ComInterfaceEntry* wrapperEntry = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DrawingCom), sizeof(ComInterfaceEntry)); + wrapperEntry->IID = IID_IStream; + wrapperEntry->Vtable = iStreamVtbl; + return wrapperEntry; + } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + Debug.Assert(obj is Interop.Ole32.IStream); + Debug.Assert(s_wrapperEntry != null); + + // Always return the same table mappings. + count = 1; + return s_wrapperEntry; + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + Debug.Assert(flags == CreateObjectFlags.UniqueInstance); + + Guid pictureIID = IPicture.IID; + int hr = Marshal.QueryInterface(externalComObject, ref pictureIID, out IntPtr comObject); + if (hr == S_OK) + { + return new PictureWrapper(comObject); + } + + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + + internal static IStreamWrapper GetComWrapper(Interop.Ole32.IStream stream) + { + IntPtr streamWrapperPtr = Instance.GetOrCreateComInterfaceForObject(stream, CreateComInterfaceFlags.None); + + Guid streamIID = IID_IStream; + int result = Marshal.QueryInterface(streamWrapperPtr, ref streamIID, out IntPtr streamPtr); + + Marshal.Release(streamWrapperPtr); + + ThrowExceptionForHR(result); + + return new IStreamWrapper(streamPtr); + } + + internal static void ThrowExceptionForHR(int errorCode) + { + // Pass -1 for errorInfo to indicate that Windows' GetErrorInfo shouldn't be called, and only + // throw the Exception corresponding to the specified errorCode. + Marshal.ThrowExceptionForHR(errorCode, errorInfo: new IntPtr(-1)); + } + + internal static class IStreamVtbl + { + public static IntPtr Create(IntPtr fpQueryInterface, IntPtr fpAddRef, IntPtr fpRelease) + { + IntPtr* vtblRaw = (IntPtr*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IStreamVtbl), IntPtr.Size * 14); + vtblRaw[0] = fpQueryInterface; + vtblRaw[1] = fpAddRef; + vtblRaw[2] = fpRelease; + vtblRaw[3] = (IntPtr)(delegate* unmanaged)&Read; + vtblRaw[4] = (IntPtr)(delegate* unmanaged)&Write; + vtblRaw[5] = (IntPtr)(delegate* unmanaged)&Seek; + vtblRaw[6] = (IntPtr)(delegate* unmanaged)&SetSize; + vtblRaw[7] = (IntPtr)(delegate* unmanaged)&CopyTo; + vtblRaw[8] = (IntPtr)(delegate* unmanaged)&Commit; + vtblRaw[9] = (IntPtr)(delegate* unmanaged)&Revert; + vtblRaw[10] = (IntPtr)(delegate* unmanaged)&LockRegion; + vtblRaw[11] = (IntPtr)(delegate* unmanaged)&UnlockRegion; + vtblRaw[12] = (IntPtr)(delegate* unmanaged)&Stat; + vtblRaw[13] = (IntPtr)(delegate* unmanaged)&Clone; + + return (IntPtr)vtblRaw; + } + + [UnmanagedCallersOnly] + private static int Read(IntPtr thisPtr, byte* pv, uint cb, uint* pcbRead) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Read(pv, cb, pcbRead); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Write(IntPtr thisPtr, byte* pv, uint cb, uint* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Write(pv, cb, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Seek(IntPtr thisPtr, long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Seek(dlibMove, dwOrigin, plibNewPosition); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int SetSize(IntPtr thisPtr, ulong libNewSize) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.SetSize(libNewSize); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int CopyTo(IntPtr thisPtr, IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + return (int)inst.CopyTo(pstm, cb, pcbRead, pcbWritten); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int Commit(IntPtr thisPtr, uint grfCommitFlags) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Commit(grfCommitFlags); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Revert(IntPtr thisPtr) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Revert(); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int LockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.LockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int UnlockRegion(IntPtr thisPtr, ulong libOffset, ulong cb, uint dwLockType) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + return (int)inst.UnlockRegion(libOffset, cb, dwLockType); + } + catch (Exception e) + { + return e.HResult; + } + } + + [UnmanagedCallersOnly] + private static int Stat(IntPtr thisPtr, Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + inst.Stat(pstatstg, grfStatFlag); + } + catch (Exception e) + { + return e.HResult; + } + + return S_OK; + } + + [UnmanagedCallersOnly] + private static int Clone(IntPtr thisPtr, IntPtr* ppstm) + { + try + { + Interop.Ole32.IStream inst = ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)thisPtr); + + return (int)inst.Clone(ppstm); + } + catch (Exception e) + { + return e.HResult; + } + } + } + + internal interface IPicture : IDisposable + { + static readonly Guid IID = new Guid(0x7BF80980, 0xBF32, 0x101A, 0x8B, 0xBB, 0, 0xAA, 0x00, 0x30, 0x0C, 0xAB); + + // NOTE: Only SaveAsFile is invoked. The other methods on IPicture are not necessary + + int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize); + } + + private sealed class PictureWrapper : IPicture + { + private readonly IntPtr _wrappedInstance; + + public PictureWrapper(IntPtr wrappedInstance) + { + _wrappedInstance = wrappedInstance; + } + + public void Dispose() + { + Marshal.Release(_wrappedInstance); + } + + public unsafe int SaveAsFile(IntPtr pstm, int fSaveMemCopy, int* pcbSize) + { + // Get the IStream implementation, since the ComWrappers runtime returns a pointer to the IUnknown interface implementation + Guid streamIID = IID_IStream; + ThrowExceptionForHR(Marshal.QueryInterface(pstm, ref streamIID, out IntPtr pstmImpl)); + + try + { + return ((delegate* unmanaged)(*(*(void***)_wrappedInstance + 15 /* IPicture.SaveAsFile slot */))) + (_wrappedInstance, pstmImpl, fSaveMemCopy, pcbSize); + } + finally + { + Marshal.Release(pstmImpl); + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/DrawingCom.cs b/src/System.Drawing.Common/src/System/Drawing/DrawingCom.cs new file mode 100644 index 00000000000..17c3ce69b1a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/DrawingCom.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal sealed partial class DrawingCom + { + internal readonly struct IStreamWrapper : IDisposable + { + public readonly IntPtr Ptr; + + public IStreamWrapper(IntPtr ptr) + { + Ptr = ptr; + } + + public void Dispose() + { + Marshal.Release(Ptr); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Font.cs b/src/System.Drawing.Common/src/System/Drawing/Font.cs new file mode 100644 index 00000000000..a008f77a3c5 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Font.cs @@ -0,0 +1,737 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Defines a particular format for text, including font face, size, and style attributes. + /// + [Editor("System.Drawing.Design.FontEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [TypeConverter(typeof(FontConverter))] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed class Font : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + private IntPtr _nativeFont; + private float _fontSize; + private FontStyle _fontStyle; + private FontFamily _fontFamily = null!; + private GraphicsUnit _fontUnit; + private byte _gdiCharSet = SafeNativeMethods.DEFAULT_CHARSET; + private bool _gdiVerticalFont; + private string _systemFontName = ""; + private string? _originalFontName; + + // Return value is in Unit (the unit the font was created in) + /// + /// Gets the size of this . + /// + public float Size => _fontSize; + + /// + /// Gets style information for this . + /// + [Browsable(false)] + public FontStyle Style => _fontStyle; + + /// + /// Gets a value indicating whether this is bold. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool Bold => (Style & FontStyle.Bold) != 0; + + /// + /// Gets a value indicating whether this is Italic. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool Italic => (Style & FontStyle.Italic) != 0; + + /// + /// Gets a value indicating whether this is strikeout (has a line through it). + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool Strikeout => (Style & FontStyle.Strikeout) != 0; + + /// + /// Gets a value indicating whether this is underlined. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool Underline => (Style & FontStyle.Underline) != 0; + + /// + /// Gets the of this . + /// + [Browsable(false)] + public FontFamily FontFamily => _fontFamily; + + /// + /// Gets the face name of this . + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Editor("System.Drawing.Design.FontNameEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [TypeConverter(typeof(FontConverter.FontNameConverter))] + public string Name => FontFamily.Name; + + /// + /// Gets the unit of measure for this . + /// + [TypeConverter(typeof(FontConverter.FontUnitConverter))] + public GraphicsUnit Unit => _fontUnit; + + /// + /// Returns the GDI char set for this instance of a font. This will only + /// be valid if this font was created from a classic GDI font definition, + /// like a LOGFONT or HFONT, or it was passed into the constructor. + /// + /// This is here for compatibility with native Win32 intrinsic controls + /// on non-Unicode platforms. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public byte GdiCharSet => _gdiCharSet; + + /// + /// Determines if this font was created to represent a GDI vertical font. This will only be valid if this font + /// was created from a classic GDIfont definition, like a LOGFONT or HFONT, or it was passed into the constructor. + /// + /// This is here for compatibility with native Win32 intrinsic controls on non-Unicode platforms. + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool GdiVerticalFont => _gdiVerticalFont; + + /// + /// This property is required by the framework and not intended to be used directly. + /// + [Browsable(false)] + public string? OriginalFontName => _originalFontName; + + /// + /// Gets the name of this . + /// + [Browsable(false)] + public string SystemFontName => _systemFontName; + + /// + /// Returns true if this is a SystemFont. + /// + [Browsable(false)] + public bool IsSystemFont => !string.IsNullOrEmpty(_systemFontName); + + /// + /// Gets the height of this . + /// + [Browsable(false)] + public int Height => (int)Math.Ceiling(GetHeight()); + + /// + /// Get native GDI+ object pointer. This property triggers the creation of the GDI+ native object if not initialized yet. + /// + internal IntPtr NativeFont => _nativeFont; + + /// + /// Cleans up Windows resources for this . + /// + ~Font() => Dispose(false); + + private Font(SerializationInfo info, StreamingContext context) + { + string name = info.GetString("Name")!; // Do not rename (binary serialization) + FontStyle style = (FontStyle)info.GetValue("Style", typeof(FontStyle))!; // Do not rename (binary serialization) + GraphicsUnit unit = (GraphicsUnit)info.GetValue("Unit", typeof(GraphicsUnit))!; // Do not rename (binary serialization) + float size = info.GetSingle("Size"); // Do not rename (binary serialization) + + Initialize(name, size, style, unit, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(name)); + } + + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) + { + string name = string.IsNullOrEmpty(OriginalFontName) ? Name : OriginalFontName; + si.AddValue("Name", name); // Do not rename (binary serialization) + si.AddValue("Size", Size); // Do not rename (binary serialization) + si.AddValue("Style", Style); // Do not rename (binary serialization) + si.AddValue("Unit", Unit); // Do not rename (binary serialization) + } + + private static bool IsVerticalName(string familyName) => familyName?.Length > 0 && familyName[0] == '@'; + + /// + /// Cleans up Windows resources for this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_nativeFont != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteFont(new HandleRef(this, _nativeFont)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsCriticalException(ex)) + { + } + finally + { + _nativeFont = IntPtr.Zero; + } + } + } + + /// + /// Returns the height of this Font in the specified graphics context. + /// + public float GetHeight(Graphics graphics) + { + ArgumentNullException.ThrowIfNull(graphics); + + float height; + int status = Gdip.GdipGetFontHeight(new HandleRef(this, NativeFont), new HandleRef(graphics, graphics.NativeGraphics), out height); + Gdip.CheckStatus(status); + + return height; + } + + public float GetHeight(float dpi) + { + float size; + int status = Gdip.GdipGetFontHeightGivenDPI(new HandleRef(this, NativeFont), dpi, out size); + Gdip.CheckStatus(status); + return size; + } + + /// + /// Returns a value indicating whether the specified object is a equivalent to this + /// . + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == this) + { + return true; + } + + if (!(obj is Font font)) + { + return false; + } + + // Note: If this and/or the passed-in font are disposed, this method can still return true since we check for cached properties + // here. + // We need to call properties on the passed-in object since it could be a proxy in a remoting scenario and proxies don't + // have access to private/internal fields. + return font.FontFamily.Equals(FontFamily) && + font.GdiVerticalFont == GdiVerticalFont && + font.GdiCharSet == GdiCharSet && + font.Style == Style && + font.Size == Size && + font.Unit == Unit; + } + + /// + /// Gets the hash code for this . + /// + public override int GetHashCode() + { + return HashCode.Combine(Name, Style, Size, Unit); + } + + /// + /// Returns a human-readable string representation of this . + /// + public override string ToString() => + $"[{GetType().Name}: Name={FontFamily.Name}, Size={_fontSize}, Units={(int)_fontUnit}, GdiCharSet={_gdiCharSet}, GdiVerticalFont={_gdiVerticalFont}]"; + + // This is used by SystemFonts when constructing a system Font objects. + internal void SetSystemFontName(string systemFontName) => _systemFontName = systemFontName; + + public unsafe void ToLogFont(object logFont, Graphics graphics) + { + ArgumentNullException.ThrowIfNull(logFont); + + Type type = logFont.GetType(); + int nativeSize = sizeof(Interop.User32.LOGFONT); + if (Marshal.SizeOf(type) != nativeSize) + { + // If we don't actually have an object that is LOGFONT in size, trying to pass + // it to GDI+ is likely to cause an AV. + throw new ArgumentException(null, nameof(logFont)); + } + + Interop.User32.LOGFONT nativeLogFont = ToLogFontInternal(graphics); + + // PtrToStructure requires that the passed in object not be a value type. + if (!type.IsValueType) + { + Marshal.PtrToStructure(new IntPtr(&nativeLogFont), logFont); + } + else + { + GCHandle handle = GCHandle.Alloc(logFont, GCHandleType.Pinned); + Buffer.MemoryCopy(&nativeLogFont, (byte*)handle.AddrOfPinnedObject(), nativeSize, nativeSize); + handle.Free(); + } + } + + private unsafe Interop.User32.LOGFONT ToLogFontInternal(Graphics graphics) + { + ArgumentNullException.ThrowIfNull(graphics); + + Interop.User32.LOGFONT logFont = default; + Gdip.CheckStatus(Gdip.GdipGetLogFontW( + new HandleRef(this, NativeFont), new HandleRef(graphics, graphics.NativeGraphics), ref logFont)); + + // Prefix the string with '@' if this is a gdiVerticalFont. + if (_gdiVerticalFont) + { + Span faceName = logFont.lfFaceName; + faceName.Slice(0, faceName.Length - 1).CopyTo(faceName.Slice(1)); + faceName[0] = '@'; + + // Docs require this to be null terminated + faceName[faceName.Length - 1] = '\0'; + } + + if (logFont.lfCharSet == 0) + { + logFont.lfCharSet = _gdiCharSet; + } + + return logFont; + } + + /// + /// Creates the GDI+ native font object. + /// + private void CreateNativeFont() + { + Debug.Assert(_nativeFont == IntPtr.Zero, "nativeFont already initialized, this will generate a handle leak."); + Debug.Assert(_fontFamily != null, "fontFamily not initialized."); + + // Note: GDI+ creates singleton font family objects (from the corresponding font file) and reference count them so + // if creating the font object from an external FontFamily, this object's FontFamily will share the same native object. + int status = Gdip.GdipCreateFont( + new HandleRef(this, _fontFamily.NativeFamily), + _fontSize, + _fontStyle, + _fontUnit, + out _nativeFont); + + // Special case this common error message to give more information + if (status == Gdip.FontStyleNotFound) + { + throw new ArgumentException(SR.Format(SR.GdiplusFontStyleNotFound, _fontFamily.Name, _fontStyle.ToString())); + } + else if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + } + + /// + /// Initializes a new instance of the class from the specified existing + /// and . + /// + public Font(Font prototype, FontStyle newStyle) + { + // Copy over the originalFontName because it won't get initialized + _originalFontName = prototype.OriginalFontName; + Initialize(prototype.FontFamily, prototype.Size, newStyle, prototype.Unit, SafeNativeMethods.DEFAULT_CHARSET, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit) + { + Initialize(family, emSize, style, unit, SafeNativeMethods.DEFAULT_CHARSET, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet) + { + Initialize(family, emSize, style, unit, gdiCharSet, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + Initialize(family, emSize, style, unit, gdiCharSet, gdiVerticalFont); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet) + { + Initialize(familyName, emSize, style, unit, gdiCharSet, IsVerticalName(familyName)); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0) + { + throw new ArgumentException(SR.Format(SR.InvalidBoundArgument, nameof(emSize), emSize, 0, "System.Single.MaxValue"), nameof(emSize)); + } + + Initialize(familyName, emSize, style, unit, gdiCharSet, gdiVerticalFont); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize, FontStyle style) + { + Initialize(family, emSize, style, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize, GraphicsUnit unit) + { + Initialize(family, emSize, FontStyle.Regular, unit, SafeNativeMethods.DEFAULT_CHARSET, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(FontFamily family, float emSize) + { + Initialize(family, emSize, FontStyle.Regular, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, false); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize, FontStyle style, GraphicsUnit unit) + { + Initialize(familyName, emSize, style, unit, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName)); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize, FontStyle style) + { + Initialize(familyName, emSize, style, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName)); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize, GraphicsUnit unit) + { + Initialize(familyName, emSize, FontStyle.Regular, unit, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName)); + } + + /// + /// Initializes a new instance of the class with the specified attributes. + /// + public Font(string familyName, float emSize) + { + Initialize(familyName, emSize, FontStyle.Regular, GraphicsUnit.Point, SafeNativeMethods.DEFAULT_CHARSET, IsVerticalName(familyName)); + } + + /// + /// Constructor to initialize fields from an existing native GDI+ object reference. Used by ToLogFont. + /// + private Font(IntPtr nativeFont, byte gdiCharSet, bool gdiVerticalFont) + { + Debug.Assert(_nativeFont == IntPtr.Zero, "GDI+ native font already initialized, this will generate a handle leak"); + Debug.Assert(nativeFont != IntPtr.Zero, "nativeFont is null"); + + _nativeFont = nativeFont; + + Gdip.CheckStatus(Gdip.GdipGetFontUnit(new HandleRef(this, nativeFont), out GraphicsUnit unit)); + Gdip.CheckStatus(Gdip.GdipGetFontSize(new HandleRef(this, nativeFont), out float size)); + Gdip.CheckStatus(Gdip.GdipGetFontStyle(new HandleRef(this, nativeFont), out FontStyle style)); + Gdip.CheckStatus(Gdip.GdipGetFamily(new HandleRef(this, nativeFont), out IntPtr nativeFamily)); + + SetFontFamily(new FontFamily(nativeFamily)); + Initialize(_fontFamily, size, style, unit, gdiCharSet, gdiVerticalFont); + } + + /// + /// Initializes this object's fields. + /// + private void Initialize(string familyName, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + _originalFontName = familyName; + + SetFontFamily(new FontFamily(StripVerticalName(familyName), createDefaultOnFail: true)); + Initialize(_fontFamily, emSize, style, unit, gdiCharSet, gdiVerticalFont); + } + + /// + /// Initializes this object's fields. + /// + private void Initialize(FontFamily family, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + ArgumentNullException.ThrowIfNull(family); + + if (float.IsNaN(emSize) || float.IsInfinity(emSize) || emSize <= 0) + { + throw new ArgumentException(SR.Format(SR.InvalidBoundArgument, nameof(emSize), emSize, 0, "System.Single.MaxValue"), nameof(emSize)); + } + + int status; + + _fontSize = emSize; + _fontStyle = style; + _fontUnit = unit; + _gdiCharSet = gdiCharSet; + _gdiVerticalFont = gdiVerticalFont; + + if (_fontFamily == null) + { + // GDI+ FontFamily is a singleton object. + SetFontFamily(new FontFamily(family.NativeFamily)); + } + + if (_nativeFont == IntPtr.Zero) + { + CreateNativeFont(); + } + + // Get actual size. + status = Gdip.GdipGetFontSize(new HandleRef(this, _nativeFont), out _fontSize); + Gdip.CheckStatus(status); + } + + /// + /// Creates a from the specified Windows handle. + /// + public static Font FromHfont(IntPtr hfont) + { + Interop.User32.LOGFONT logFont = default; + Interop.Gdi32.GetObject(new HandleRef(null, hfont), ref logFont); + + using (ScreenDC dc = ScreenDC.Create()) + { + return FromLogFontInternal(ref logFont, dc); + } + } + + /// + /// Creates a from the given LOGFONT using the screen device context. + /// + /// A boxed LOGFONT. + /// The newly created . + public static Font FromLogFont(object lf) + { + using (ScreenDC dc = ScreenDC.Create()) + { + return FromLogFont(lf, dc); + } + } + + internal static Font FromLogFont(ref Interop.User32.LOGFONT logFont) + { + using (ScreenDC dc = ScreenDC.Create()) + { + return FromLogFont(logFont, dc); + } + } + + internal static Font FromLogFontInternal(ref Interop.User32.LOGFONT logFont, IntPtr hdc) + { + int status = Gdip.GdipCreateFontFromLogfontW(hdc, ref logFont, out IntPtr font); + + // Special case this incredibly common error message to give more information + if (status == Gdip.NotTrueTypeFont) + { + throw new ArgumentException(SR.GdiplusNotTrueTypeFont_NoName); + } + else if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + // GDI+ returns font = 0 even though the status is Ok. + if (font == IntPtr.Zero) + { + throw new ArgumentException(SR.Format(SR.GdiplusNotTrueTypeFont, logFont.ToString())); + } + + bool gdiVerticalFont = logFont.lfFaceName[0] == '@'; + return new Font(font, logFont.lfCharSet, gdiVerticalFont); + } + + /// + /// Creates a from the given LOGFONT using the given device context. + /// + /// A boxed LOGFONT. + /// Handle to a device context (HDC). + /// The newly created . + public static unsafe Font FromLogFont(object lf, IntPtr hdc) + { + ArgumentNullException.ThrowIfNull(lf); + + if (lf is Interop.User32.LOGFONT logFont) + { + // A boxed LOGFONT, just use it to create the font + return FromLogFontInternal(ref logFont, hdc); + } + + Type type = lf.GetType(); + int nativeSize = sizeof(Interop.User32.LOGFONT); + if (Marshal.SizeOf(type) != nativeSize) + { + // If we don't actually have an object that is LOGFONT in size, trying to pass + // it to GDI+ is likely to cause an AV. + throw new ArgumentException(null, nameof(lf)); + } + + // Now that we know the marshalled size is the same as LOGFONT, copy in the data + logFont = default; + + Marshal.StructureToPtr(lf, new IntPtr(&logFont), fDeleteOld: false); + + return FromLogFontInternal(ref logFont, hdc); + } + + /// + /// Creates a from the specified handle to a device context (HDC). + /// + /// The newly created . + public static Font FromHdc(IntPtr hdc) + { + IntPtr font = IntPtr.Zero; + int status = Gdip.GdipCreateFontFromDC(hdc, ref font); + + // Special case this incredibly common error message to give more information + if (status == Gdip.NotTrueTypeFont) + { + throw new ArgumentException(SR.GdiplusNotTrueTypeFont_NoName); + } + else if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + return new Font(font, 0, false); + } + + /// + /// Creates an exact copy of this . + /// + public object Clone() + { + int status = Gdip.GdipCloneFont(new HandleRef(this, _nativeFont), out IntPtr clonedFont); + Gdip.CheckStatus(status); + + return new Font(clonedFont, _gdiCharSet, _gdiVerticalFont); + } + + private void SetFontFamily(FontFamily family) + { + _fontFamily = family; + + // GDI+ creates ref-counted singleton FontFamily objects based on the family name so all managed + // objects with same family name share the underlying GDI+ native pointer. The unmanged object is + // destroyed when its ref-count gets to zero. + // + // Make sure _fontFamily is not finalized so the underlying singleton object is kept alive. + GC.SuppressFinalize(_fontFamily); + } + + [return: NotNullIfNotNull(nameof(familyName))] + private static string? StripVerticalName(string? familyName) + { + if (familyName?.Length > 1 && familyName[0] == '@') + { + return familyName.Substring(1); + } + + return familyName; + } + + public void ToLogFont(object logFont) + { + using (ScreenDC dc = ScreenDC.Create()) + using (Graphics graphics = Graphics.FromHdcInternal(dc)) + { + ToLogFont(logFont, graphics); + } + } + + /// + /// Returns a handle to this . + /// + public IntPtr ToHfont() + { + using (ScreenDC dc = ScreenDC.Create()) + using (Graphics graphics = Graphics.FromHdcInternal(dc)) + { + Interop.User32.LOGFONT lf = ToLogFontInternal(graphics); + IntPtr handle = Interop.Gdi32.CreateFontIndirectW(ref lf); + if (handle == IntPtr.Zero) + { + throw new Win32Exception(); + } + + return handle; + } + } + + public float GetHeight() + { + using (ScreenDC dc = ScreenDC.Create()) + using (Graphics graphics = Graphics.FromHdcInternal(dc)) + { + return GetHeight(graphics); + } + } + + /// + /// Gets the size, in points, of this . + /// + [Browsable(false)] + public float SizeInPoints + { + get + { + if (Unit == GraphicsUnit.Point) + { + return Size; + } + + using (ScreenDC dc = ScreenDC.Create()) + using (Graphics graphics = Graphics.FromHdcInternal(dc)) + { + float pixelsPerPoint = (float)(graphics.DpiY / 72.0); + float lineSpacingInPixels = GetHeight(graphics); + float emHeightInPixels = lineSpacingInPixels * FontFamily.GetEmHeight(Style) / FontFamily.GetLineSpacing(Style); + + return emHeightInPixels / pixelsPerPoint; + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/FontConverter.cs b/src/System.Drawing.Common/src/System/Drawing/FontConverter.cs new file mode 100644 index 00000000000..3897358d9e3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/FontConverter.cs @@ -0,0 +1,463 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Text; +using System.Globalization; +using System.Reflection; +using System.Text; + +namespace System.Drawing +{ + public class FontConverter : TypeConverter + { + private const string StylePrefix = "style="; + + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) + { + return (destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor)); + } + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (value is Font font) + { + if (destinationType == typeof(string)) + { + culture ??= CultureInfo.CurrentCulture; + + ValueStringBuilder sb = default; + sb.Append(font.Name); + sb.Append(culture.TextInfo.ListSeparator[0]); + sb.Append(' '); + sb.Append(font.Size.ToString(culture.NumberFormat)); + + switch (font.Unit) + { + // MS throws ArgumentException, if unit is set + // to GraphicsUnit.Display + // Don't know what to append for GraphicsUnit.Display + case GraphicsUnit.Display: + sb.Append("display"); + break; + + case GraphicsUnit.Document: + sb.Append("doc"); + break; + + case GraphicsUnit.Point: + sb.Append("pt"); + break; + + case GraphicsUnit.Inch: + sb.Append("in"); + break; + + case GraphicsUnit.Millimeter: + sb.Append("mm"); + break; + + case GraphicsUnit.Pixel: + sb.Append("px"); + break; + + case GraphicsUnit.World: + sb.Append("world"); + break; + } + + if (font.Style != FontStyle.Regular) + { + sb.Append(culture.TextInfo.ListSeparator[0]); + sb.Append(" style="); + sb.Append(font.Style.ToString()); + } + + return sb.ToString(); + } + + if (destinationType == typeof(InstanceDescriptor)) + { + ConstructorInfo? met = typeof(Font).GetConstructor(new Type[] { typeof(string), typeof(float), typeof(FontStyle), typeof(GraphicsUnit) }); + object[] args = new object[4]; + args[0] = font.Name; + args[1] = font.Size; + args[2] = font.Style; + args[3] = font.Unit; + + return new InstanceDescriptor(met, args); + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + if (!(value is string font)) + { + return base.ConvertFrom(context, culture, value); + } + + font = font.Trim(); + + // Expected string format: "name[, size[, units[, style=style1[, style2[...]]]]]" + // Example using 'vi-VN' culture: "Microsoft Sans Serif, 8,25pt, style=Italic, Bold" + if (font.Length == 0) + { + return null; + } + + culture ??= CultureInfo.CurrentCulture; + + char separator = culture.TextInfo.ListSeparator[0]; // For vi-VN: ',' + string fontName = font; // start with the assumption that only the font name was provided. + string? style = null; + string? sizeStr; + float fontSize = 8.25f; + FontStyle fontStyle = FontStyle.Regular; + GraphicsUnit units = GraphicsUnit.Point; + + // Get the index of the first separator (would indicate the end of the name in the string). + int nameIndex = font.IndexOf(separator); + + if (nameIndex < 0) + { + return new Font(fontName, fontSize, fontStyle, units); + } + + // Some parameters are provided in addition to name. + fontName = font.Substring(0, nameIndex); + + if (nameIndex < font.Length - 1) + { + // Get the style index (if any). The size is a bit problematic because it can be formatted differently + // depending on the culture, we'll parse it last. + int styleIndex = culture.CompareInfo.IndexOf(font, StylePrefix, CompareOptions.IgnoreCase); + + if (styleIndex != -1) + { + // style found. + style = font.Substring(styleIndex); + + // Get the mid-substring containing the size information. + sizeStr = font.Substring(nameIndex + 1, styleIndex - nameIndex - 1); + } + else + { + // no style. + sizeStr = font.Substring(nameIndex + 1); + } + + // Parse size. + (string? size, string? unit) unitTokens = ParseSizeTokens(sizeStr, separator); + + if (unitTokens.size != null) + { + try + { + fontSize = (float)GetFloatConverter().ConvertFromString(context, culture, unitTokens.size)!; + } + catch + { + // Exception from converter is too generic. + throw new ArgumentException(SR.Format(SR.TextParseFailedFormat, font, $"name{separator} size[units[{separator} style=style1[{separator} style2{separator} ...]]]"), nameof(value)); + } + } + + if (unitTokens.unit != null) + { + // ParseGraphicsUnits throws an ArgumentException if format is invalid. + units = ParseGraphicsUnits(unitTokens.unit); + } + + if (style != null) + { + // Parse FontStyle + style = style.Substring(6); // style string always starts with style= + string[] styleTokens = style.Split(separator); + + for (int tokenCount = 0; tokenCount < styleTokens.Length; tokenCount++) + { + string styleText = styleTokens[tokenCount]; + styleText = styleText.Trim(); + + fontStyle |= Enum.Parse(styleText, true); + + // Enum.IsDefined doesn't do what we want on flags enums... + FontStyle validBits = FontStyle.Regular | FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout; + if ((fontStyle | validBits) != validBits) + { + throw new InvalidEnumArgumentException(nameof(style), (int)fontStyle, typeof(FontStyle)); + } + } + } + } + + return new Font(fontName, fontSize, fontStyle, units); + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "TypeDescriptor.GetConverter is safe for primitive types.")] + static TypeConverter GetFloatConverter() => TypeDescriptor.GetConverter(typeof(float)); + } + + private static (string?, string?) ParseSizeTokens(string text, char separator) + { + string? size = null; + string? units = null; + + text = text.Trim(); + + int length = text.Length; + int splitPoint; + + if (length > 0) + { + // text is expected to have a format like " 8,25pt, ". Leading and trailing spaces (trimmed above), + // last comma, unit and decimal value may not appear. We need to make it ####.##CC + for (splitPoint = 0; splitPoint < length; splitPoint++) + { + if (char.IsLetter(text[splitPoint])) + { + break; + } + } + + char[] trimChars = new char[] { separator, ' ' }; + + if (splitPoint > 0) + { + size = text.Substring(0, splitPoint); + // Trimming spaces between size and units. + size = size.Trim(trimChars); + } + + if (splitPoint < length) + { + units = text.Substring(splitPoint); + units = units.TrimEnd(trimChars); + } + } + + return (size, units); + } + + private static GraphicsUnit ParseGraphicsUnits(string units) => + units switch + { + "display" => GraphicsUnit.Display, + "doc" => GraphicsUnit.Document, + "pt" => GraphicsUnit.Point, + "in" => GraphicsUnit.Inch, + "mm" => GraphicsUnit.Millimeter, + "px" => GraphicsUnit.Pixel, + "world" => GraphicsUnit.World, + _ => throw new ArgumentException(SR.Format(SR.InvalidArgumentValueFontConverter, units), nameof(units)), + }; + + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) + { + ArgumentNullException.ThrowIfNull(propertyValues); + + object? value; + byte charSet = 1; + float size = 8; + string? name = null; + bool vertical = false; + FontStyle style = FontStyle.Regular; + FontFamily? fontFamily = null; + GraphicsUnit unit = GraphicsUnit.Point; + + if ((value = propertyValues["GdiCharSet"]) != null) + charSet = (byte)value; + + if ((value = propertyValues["Size"]) != null) + size = (float)value; + + if ((value = propertyValues["Unit"]) != null) + unit = (GraphicsUnit)value; + + if ((value = propertyValues["Name"]) != null) + name = (string)value; + + if ((value = propertyValues["GdiVerticalFont"]) != null) + vertical = (bool)value; + + if ((value = propertyValues["Bold"]) != null) + { + if ((bool)value) + style |= FontStyle.Bold; + } + + if ((value = propertyValues["Italic"]) != null) + { + if ((bool)value) + style |= FontStyle.Italic; + } + + if ((value = propertyValues["Strikeout"]) != null) + { + if ((bool)value) + style |= FontStyle.Strikeout; + } + + if ((value = propertyValues["Underline"]) != null) + { + if ((bool)value) + style |= FontStyle.Underline; + } + + if (name == null) + { + fontFamily = new FontFamily("Tahoma"); + } + else + { + FontCollection collection = new InstalledFontCollection(); + FontFamily[] installedFontList = collection.Families; + foreach (FontFamily font in installedFontList) + { + if (name.Equals(font.Name, StringComparison.OrdinalIgnoreCase)) + { + fontFamily = font; + break; + } + } + + // font family not found in installed fonts + if (fontFamily == null) + { + collection = new PrivateFontCollection(); + FontFamily[] privateFontList = collection.Families; + foreach (FontFamily font in privateFontList) + { + if (name.Equals(font.Name, StringComparison.OrdinalIgnoreCase)) + { + fontFamily = font; + break; + } + } + } + + // font family not found in private fonts also + fontFamily ??= FontFamily.GenericSansSerif; + } + + return new Font(fontFamily, size, style, unit, charSet, vertical); + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; + + [RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + public override PropertyDescriptorCollection? GetProperties( + ITypeDescriptorContext? context, + object value, + Attribute[]? attributes) + { + if (value is not Font) + return base.GetProperties(context, value, attributes); + + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(value, attributes); + return props.Sort(new string[] { nameof(Font.Name), nameof(Font.Size), nameof(Font.Unit) }); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; + + public sealed class FontNameConverter : TypeConverter, IDisposable + { + private readonly FontFamily[] _fonts; + + public FontNameConverter() + { + _fonts = FontFamily.Families; + } + + void IDisposable.Dispose() + { + } + + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + return value is string strValue ? MatchFontName(strValue, context) : base.ConvertFrom(context, culture, value); + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) + { + string[] values = new string[_fonts.Length]; + for (int i = 0; i < _fonts.Length; i++) + { + values[i] = _fonts[i].Name; + } + Array.Sort(values, Comparer.Default); + + return new TypeConverter.StandardValuesCollection(values); + } + + // We allow other values other than those in the font list. + public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context) => false; + + // Yes, we support picking an element from the list. + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; + + private string MatchFontName(string name, ITypeDescriptorContext? context) + { + // Try a partial match + string? bestMatch = null; + + // setting fontName as nullable since IEnumerable.Current returned nullable in 3.0 + foreach (string? fontName in GetStandardValues(context)) + { + Debug.Assert(fontName != null); + if (fontName.Equals(name, StringComparison.InvariantCultureIgnoreCase)) + { + // For an exact match, return immediately + return fontName; + } + if (fontName.StartsWith(name, StringComparison.InvariantCultureIgnoreCase)) + { + if (bestMatch == null || fontName.Length <= bestMatch.Length) + { + bestMatch = fontName; + } + } + } + + // No match... fall back on whatever was provided + return bestMatch ?? name; + } + } + + public class FontUnitConverter : EnumConverter + { + public FontUnitConverter() : base(typeof(GraphicsUnit)) { } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) + { + // display graphic unit is not supported. + if (Values == null) + { + base.GetStandardValues(context); // sets "values" + Debug.Assert(Values != null); + ArrayList filteredValues = new ArrayList(Values); + filteredValues.Remove(GraphicsUnit.Display); + Values = new StandardValuesCollection(filteredValues); + } + return Values; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs b/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs new file mode 100644 index 00000000000..7991b1ad6b6 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/FontFamily.cs @@ -0,0 +1,315 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Text; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Abstracts a group of type faces having a similar basic design but having certain variation in styles. + /// + public sealed class FontFamily : MarshalByRefObject, IDisposable + { + private const int NeutralLanguage = 0; + private IntPtr _nativeFamily; + private readonly bool _createDefaultOnFail; + +#if DEBUG + private static readonly object s_lockObj = new object(); + private static int s_idCount; + private int _id; +#endif + + private void SetNativeFamily(IntPtr family) + { + Debug.Assert(_nativeFamily == IntPtr.Zero, "Setting GDI+ native font family when already initialized."); + + _nativeFamily = family; +#if DEBUG + lock (s_lockObj) + { + _id = ++s_idCount; + } +#endif + } + + internal FontFamily(IntPtr family) => SetNativeFamily(family); + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// The parameter determines how errors are handled when creating a + /// font based on a font family that does not exist on the end user's system at run time. If this parameter is + /// true, then a fall-back font will always be used instead. If this parameter is false, an exception will be thrown. + /// + internal FontFamily(string name, bool createDefaultOnFail) + { + _createDefaultOnFail = createDefaultOnFail; + CreateFontFamily(name, null); + } + + /// + /// Initializes a new instance of the class with the specified name. + /// + public FontFamily(string name) => CreateFontFamily(name, null); + + /// + /// Initializes a new instance of the class in the specified + /// and with the specified name. + /// + public FontFamily(string name, FontCollection? fontCollection) => CreateFontFamily(name, fontCollection); + + // Creates the native font family object. + // Note: GDI+ creates singleton font family objects (from the corresponding font file) and reference count them. + private void CreateFontFamily(string name, FontCollection? fontCollection) + { + IntPtr fontfamily; + IntPtr nativeFontCollection = (fontCollection == null) ? IntPtr.Zero : fontCollection._nativeFontCollection; + + int status = Gdip.GdipCreateFontFamilyFromName(name, new HandleRef(fontCollection, nativeFontCollection), out fontfamily); + + if (status != Gdip.Ok) + { + if (_createDefaultOnFail) + { + fontfamily = GetGdipGenericSansSerif(); // This throws if failed. + } + else + { + // Special case this incredibly common error message to give more information. + if (status == Gdip.FontFamilyNotFound) + { + throw new ArgumentException(SR.Format(SR.GdiplusFontFamilyNotFound, name)); + } + else if (status == Gdip.NotTrueTypeFont) + { + throw new ArgumentException(SR.Format(SR.GdiplusNotTrueTypeFont, name)); + } + else + { + throw Gdip.StatusException(status); + } + } + } + + SetNativeFamily(fontfamily); + } + + /// + /// Initializes a new instance of the class from the specified generic font family. + /// + public FontFamily(GenericFontFamilies genericFamily) + { + IntPtr nativeFamily; + int status; + + switch (genericFamily) + { + case GenericFontFamilies.Serif: + status = Gdip.GdipGetGenericFontFamilySerif(out nativeFamily); + break; + case GenericFontFamilies.SansSerif: + status = Gdip.GdipGetGenericFontFamilySansSerif(out nativeFamily); + break; + case GenericFontFamilies.Monospace: + default: + status = Gdip.GdipGetGenericFontFamilyMonospace(out nativeFamily); + break; + } + Gdip.CheckStatus(status); + + SetNativeFamily(nativeFamily); + } + + ~FontFamily() => Dispose(false); + + internal IntPtr NativeFamily => _nativeFamily; + + /// + /// Converts this to a human-readable string. + /// + public override string ToString() => $"[{GetType().Name}: Name={Name}]"; + + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (obj == this) + { + return true; + } + + // if obj = null then (obj is FontFamily) = false. + if (!(obj is FontFamily otherFamily)) + { + return false; + } + + // We can safely use the ptr to the native GDI+ FontFamily because in windows it is common to + // all objects of the same family (singleton RO object). + return otherFamily.NativeFamily == NativeFamily; + } + + /// + /// Gets a hash code for this . + /// + public override int GetHashCode() => GetName(NeutralLanguage).GetHashCode(); + + private static int CurrentLanguage => CultureInfo.CurrentUICulture.LCID; + + /// + /// Disposes of this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_nativeFamily != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteFontFamily(new HandleRef(this, _nativeFamily)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsCriticalException(ex)) + { + } + finally + { + _nativeFamily = IntPtr.Zero; + } + } + } + + /// + /// Gets the name of this . + /// + public string Name => GetName(CurrentLanguage); + + /// + /// Returns the name of this in the specified language. + /// + public unsafe string GetName(int language) + { + char* name = stackalloc char[32]; // LF_FACESIZE is 32 + int status = Gdip.GdipGetFamilyName(new HandleRef(this, NativeFamily), name, language); + Gdip.CheckStatus(status); + return Marshal.PtrToStringUni((IntPtr)name)!; + } + + /// + /// Returns an array that contains all of the objects associated with the current + /// graphics context. + /// + public static FontFamily[] Families => new InstalledFontCollection().Families; + + /// + /// Gets a generic SansSerif . + /// + public static FontFamily GenericSansSerif => new FontFamily(GetGdipGenericSansSerif()); + + private static IntPtr GetGdipGenericSansSerif() + { + IntPtr nativeFamily; + int status = Gdip.GdipGetGenericFontFamilySansSerif(out nativeFamily); + Gdip.CheckStatus(status); + + return nativeFamily; + } + + /// + /// Gets a generic Serif . + /// + public static FontFamily GenericSerif => new FontFamily(GenericFontFamilies.Serif); + + /// + /// Gets a generic monospace . + /// + public static FontFamily GenericMonospace => new FontFamily(GenericFontFamilies.Monospace); + + /// + /// Returns an array that contains all of the objects associated with the specified + /// graphics context. + /// + [Obsolete("FontFamily.GetFamilies has been deprecated. Use Families instead.")] + public static FontFamily[] GetFamilies(Graphics graphics) + { + ArgumentNullException.ThrowIfNull(graphics); + + return new InstalledFontCollection().Families; + } + + /// + /// Indicates whether the specified is available. + /// + public bool IsStyleAvailable(FontStyle style) + { + int bresult; + int status = Gdip.GdipIsStyleAvailable(new HandleRef(this, NativeFamily), style, out bresult); + Gdip.CheckStatus(status); + + return bresult != 0; + } + + /// + /// Gets the size of the Em square for the specified style in font design units. + /// + public int GetEmHeight(FontStyle style) + { + int result; + int status = Gdip.GdipGetEmHeight(new HandleRef(this, NativeFamily), style, out result); + Gdip.CheckStatus(status); + + return result; + } + + /// + /// Returns the ascender metric for Windows. + /// + public int GetCellAscent(FontStyle style) + { + int result; + int status = Gdip.GdipGetCellAscent(new HandleRef(this, NativeFamily), style, out result); + Gdip.CheckStatus(status); + + return result; + } + + /// + /// Returns the descender metric for Windows. + /// + public int GetCellDescent(FontStyle style) + { + int result; + int status = Gdip.GdipGetCellDescent(new HandleRef(this, NativeFamily), style, out result); + Gdip.CheckStatus(status); + + return result; + } + + /// + /// Returns the distance between two consecutive lines of text for this with the + /// specified . + /// + public int GetLineSpacing(FontStyle style) + { + int result; + int status = Gdip.GdipGetLineSpacing(new HandleRef(this, NativeFamily), style, out result); + Gdip.CheckStatus(status); + + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/FontStyle.cs b/src/System.Drawing.Common/src/System/Drawing/FontStyle.cs new file mode 100644 index 00000000000..3d380bdfe5c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/FontStyle.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies style information applied to text. + /// + [Flags] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public enum FontStyle + { + /// + /// Normal text. + /// + Regular = 0, + /// + /// Bold text. + /// + Bold = 1, + /// + /// Italic text. + /// + Italic = 2, + /// + /// Underlined text. + /// + Underline = 4, + /// + /// Text with a line through the middle. + /// + Strikeout = 8, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs b/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs new file mode 100644 index 00000000000..723e2804a88 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Gdiplus.cs @@ -0,0 +1,426 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal static partial class SafeNativeMethods + { + // We make this a nested class so that we don't have to initialize GDI+ to access SafeNativeMethods (mostly gdi/user32). + internal static partial class Gdip + { + private static readonly IntPtr s_initToken; + + [ThreadStatic] + private static IDictionary? t_threadData; + + static Gdip() + { + if (!OperatingSystem.IsWindows()) + { + NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), static (_, _, _) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_Unix)); + } + + Debug.Assert(s_initToken == IntPtr.Zero, "GdiplusInitialization: Initialize should not be called more than once in the same domain!"); + + // GDI+ ref counts multiple calls to Startup in the same process, so calls from multiple + // domains are ok, just make sure to pair each w/GdiplusShutdown + int status = GdiplusStartup(out s_initToken, StartupInputEx.GetDefault(), out _); + CheckStatus(status); + } + + /// + /// Returns true if GDI+ has been started, but not shut down + /// + internal static bool Initialized => s_initToken != IntPtr.Zero; + + /// + /// This property will give us back a hashtable we can use to store all of our static brushes and pens on + /// a per-thread basis. This way we can avoid 'object in use' crashes when different threads are + /// referencing the same drawing object. + /// + internal static IDictionary ThreadData => t_threadData ??= new Hashtable(); + + // Used to ensure static constructor has run. + internal static void DummyFunction() + { + } + + //---------------------------------------------------------------------------------------- + // Status codes + //---------------------------------------------------------------------------------------- + internal const int Ok = 0; + internal const int GenericError = 1; + internal const int InvalidParameter = 2; + internal const int OutOfMemory = 3; + internal const int ObjectBusy = 4; + internal const int InsufficientBuffer = 5; + internal const int NotImplemented = 6; + internal const int Win32Error = 7; + internal const int WrongState = 8; + internal const int Aborted = 9; + internal const int FileNotFound = 10; + internal const int ValueOverflow = 11; + internal const int AccessDenied = 12; + internal const int UnknownImageFormat = 13; + internal const int FontFamilyNotFound = 14; + internal const int FontStyleNotFound = 15; + internal const int NotTrueTypeFont = 16; + internal const int UnsupportedGdiplusVersion = 17; + internal const int GdiplusNotInitialized = 18; + internal const int PropertyNotFound = 19; + internal const int PropertyNotSupported = 20; + + internal static void CheckStatus(int status) + { + if (status != Ok) + throw StatusException(status); + } + + internal static Exception StatusException(int status) + { + Debug.Assert(status != Ok, "Throwing an exception for an 'Ok' return code"); + + switch (status) + { + case GenericError: + return new ExternalException(SR.GdiplusGenericError, E_FAIL); + case InvalidParameter: + return new ArgumentException(SR.GdiplusInvalidParameter); + case OutOfMemory: + return new OutOfMemoryException(SR.GdiplusOutOfMemory); + case ObjectBusy: + return new InvalidOperationException(SR.GdiplusObjectBusy); + case InsufficientBuffer: + return new OutOfMemoryException(SR.GdiplusInsufficientBuffer); + case NotImplemented: + return new NotImplementedException(SR.GdiplusNotImplemented); + case Win32Error: + return new ExternalException(SR.GdiplusGenericError, E_FAIL); + case WrongState: + return new InvalidOperationException(SR.GdiplusWrongState); + case Aborted: + return new ExternalException(SR.GdiplusAborted, E_ABORT); + case FileNotFound: + return new FileNotFoundException(SR.GdiplusFileNotFound); + case ValueOverflow: + return new OverflowException(SR.GdiplusOverflow); + case AccessDenied: + return new ExternalException(SR.GdiplusAccessDenied, E_ACCESSDENIED); + case UnknownImageFormat: + return new ArgumentException(SR.GdiplusUnknownImageFormat); + case PropertyNotFound: + return new ArgumentException(SR.GdiplusPropertyNotFoundError); + case PropertyNotSupported: + return new ArgumentException(SR.GdiplusPropertyNotSupportedError); + + case FontFamilyNotFound: + Debug.Fail("We should be special casing FontFamilyNotFound so we can provide the font name"); + return new ArgumentException(SR.Format(SR.GdiplusFontFamilyNotFound, "?")); + + case FontStyleNotFound: + Debug.Fail("We should be special casing FontStyleNotFound so we can provide the font name"); + return new ArgumentException(SR.Format(SR.GdiplusFontStyleNotFound, "?", "?")); + + case NotTrueTypeFont: + Debug.Fail("We should be special casing NotTrueTypeFont so we can provide the font name"); + return new ArgumentException(SR.GdiplusNotTrueTypeFont_NoName); + + case UnsupportedGdiplusVersion: + return new ExternalException(SR.GdiplusUnsupportedGdiplusVersion, E_FAIL); + + case GdiplusNotInitialized: + return new ExternalException(SR.GdiplusNotInitialized, E_FAIL); + } + + return new ExternalException($"{SR.GdiplusUnknown} [{status}]", E_UNEXPECTED); + } + } + + public const int + E_UNEXPECTED = unchecked((int)0x8000FFFF), + E_NOTIMPL = unchecked((int)0x80004001), + E_ABORT = unchecked((int)0x80004004), + E_FAIL = unchecked((int)0x80004005), + E_ACCESSDENIED = unchecked((int)0x80070005), + GMEM_MOVEABLE = 0x0002, + GMEM_ZEROINIT = 0x0040, + DM_IN_BUFFER = 8, + DM_OUT_BUFFER = 2, + DC_PAPERS = 2, + DC_PAPERSIZE = 3, + DC_BINS = 6, + DC_DUPLEX = 7, + DC_BINNAMES = 12, + DC_ENUMRESOLUTIONS = 13, + DC_PAPERNAMES = 16, + DC_ORIENTATION = 17, + DC_COPIES = 18, + DC_COLORDEVICE = 32, + PD_ALLPAGES = 0x00000000, + PD_SELECTION = 0x00000001, + PD_PAGENUMS = 0x00000002, + PD_CURRENTPAGE = 0x00400000, + PD_RETURNDEFAULT = 0x00000400, + DI_NORMAL = 0x0003, + IMAGE_ICON = 1, + IDI_APPLICATION = 32512, + IDI_HAND = 32513, + IDI_QUESTION = 32514, + IDI_EXCLAMATION = 32515, + IDI_ASTERISK = 32516, + IDI_WINLOGO = 32517, + IDI_WARNING = 32515, + IDI_ERROR = 32513, + IDI_INFORMATION = 32516, + DM_ORIENTATION = 0x00000001, + DM_PAPERSIZE = 0x00000002, + DM_PAPERLENGTH = 0x00000004, + DM_PAPERWIDTH = 0x00000008, + DM_COPIES = 0x00000100, + DM_DEFAULTSOURCE = 0x00000200, + DM_PRINTQUALITY = 0x00000400, + DM_COLOR = 0x00000800, + DM_DUPLEX = 0x00001000, + DM_YRESOLUTION = 0x00002000, + DM_COLLATE = 0x00008000, + DMORIENT_PORTRAIT = 1, + DMORIENT_LANDSCAPE = 2, + DMPAPER_LETTER = 1, + DMPAPER_LETTERSMALL = 2, + DMPAPER_TABLOID = 3, + DMPAPER_LEDGER = 4, + DMPAPER_LEGAL = 5, + DMPAPER_STATEMENT = 6, + DMPAPER_EXECUTIVE = 7, + DMPAPER_A3 = 8, + DMPAPER_A4 = 9, + DMPAPER_A4SMALL = 10, + DMPAPER_A5 = 11, + DMPAPER_B4 = 12, + DMPAPER_B5 = 13, + DMPAPER_FOLIO = 14, + DMPAPER_QUARTO = 15, + DMPAPER_10X14 = 16, + DMPAPER_11X17 = 17, + DMPAPER_NOTE = 18, + DMPAPER_ENV_9 = 19, + DMPAPER_ENV_10 = 20, + DMPAPER_ENV_11 = 21, + DMPAPER_ENV_12 = 22, + DMPAPER_ENV_14 = 23, + DMPAPER_CSHEET = 24, + DMPAPER_DSHEET = 25, + DMPAPER_ESHEET = 26, + DMPAPER_ENV_DL = 27, + DMPAPER_ENV_C5 = 28, + DMPAPER_ENV_C3 = 29, + DMPAPER_ENV_C4 = 30, + DMPAPER_ENV_C6 = 31, + DMPAPER_ENV_C65 = 32, + DMPAPER_ENV_B4 = 33, + DMPAPER_ENV_B5 = 34, + DMPAPER_ENV_B6 = 35, + DMPAPER_ENV_ITALY = 36, + DMPAPER_ENV_MONARCH = 37, + DMPAPER_ENV_PERSONAL = 38, + DMPAPER_FANFOLD_US = 39, + DMPAPER_FANFOLD_STD_GERMAN = 40, + DMPAPER_FANFOLD_LGL_GERMAN = 41, + DMPAPER_ISO_B4 = 42, + DMPAPER_JAPANESE_POSTCARD = 43, + DMPAPER_9X11 = 44, + DMPAPER_10X11 = 45, + DMPAPER_15X11 = 46, + DMPAPER_ENV_INVITE = 47, + DMPAPER_RESERVED_48 = 48, + DMPAPER_RESERVED_49 = 49, + DMPAPER_LETTER_EXTRA = 50, + DMPAPER_LEGAL_EXTRA = 51, + DMPAPER_TABLOID_EXTRA = 52, + DMPAPER_A4_EXTRA = 53, + DMPAPER_LETTER_TRANSVERSE = 54, + DMPAPER_A4_TRANSVERSE = 55, + DMPAPER_LETTER_EXTRA_TRANSVERSE = 56, + DMPAPER_A_PLUS = 57, + DMPAPER_B_PLUS = 58, + DMPAPER_LETTER_PLUS = 59, + DMPAPER_A4_PLUS = 60, + DMPAPER_A5_TRANSVERSE = 61, + DMPAPER_B5_TRANSVERSE = 62, + DMPAPER_A3_EXTRA = 63, + DMPAPER_A5_EXTRA = 64, + DMPAPER_B5_EXTRA = 65, + DMPAPER_A2 = 66, + DMPAPER_A3_TRANSVERSE = 67, + DMPAPER_A3_EXTRA_TRANSVERSE = 68, + + // WINVER >= 0x0500 + DMPAPER_DBL_JAPANESE_POSTCARD = 69, /* Japanese Double Postcard 200 x 148 mm */ + DMPAPER_A6 = 70, /* A6 105 x 148 mm */ + DMPAPER_JENV_KAKU2 = 71, /* Japanese Envelope Kaku #2 */ + DMPAPER_JENV_KAKU3 = 72, /* Japanese Envelope Kaku #3 */ + DMPAPER_JENV_CHOU3 = 73, /* Japanese Envelope Chou #3 */ + DMPAPER_JENV_CHOU4 = 74, /* Japanese Envelope Chou #4 */ + DMPAPER_LETTER_ROTATED = 75, /* Letter Rotated 11 x 8 1/2 11 in */ + DMPAPER_A3_ROTATED = 76, /* A3 Rotated 420 x 297 mm */ + DMPAPER_A4_ROTATED = 77, /* A4 Rotated 297 x 210 mm */ + DMPAPER_A5_ROTATED = 78, /* A5 Rotated 210 x 148 mm */ + DMPAPER_B4_JIS_ROTATED = 79, /* B4 (JIS) Rotated 364 x 257 mm */ + DMPAPER_B5_JIS_ROTATED = 80, /* B5 (JIS) Rotated 257 x 182 mm */ + DMPAPER_JAPANESE_POSTCARD_ROTATED = 81, /* Japanese Postcard Rotated 148 x 100 mm */ + DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED = 82, /* Double Japanese Postcard Rotated 148 x 200 mm */ + DMPAPER_A6_ROTATED = 83, /* A6 Rotated 148 x 105 mm */ + DMPAPER_JENV_KAKU2_ROTATED = 84, /* Japanese Envelope Kaku #2 Rotated */ + DMPAPER_JENV_KAKU3_ROTATED = 85, /* Japanese Envelope Kaku #3 Rotated */ + DMPAPER_JENV_CHOU3_ROTATED = 86, /* Japanese Envelope Chou #3 Rotated */ + DMPAPER_JENV_CHOU4_ROTATED = 87, /* Japanese Envelope Chou #4 Rotated */ + DMPAPER_B6_JIS = 88, /* B6 (JIS) 128 x 182 mm */ + DMPAPER_B6_JIS_ROTATED = 89, /* B6 (JIS) Rotated 182 x 128 mm */ + DMPAPER_12X11 = 90, /* 12 x 11 in */ + DMPAPER_JENV_YOU4 = 91, /* Japanese Envelope You #4 */ + DMPAPER_JENV_YOU4_ROTATED = 92, /* Japanese Envelope You #4 Rotated*/ + DMPAPER_P16K = 93, /* PRC 16K 146 x 215 mm */ + DMPAPER_P32K = 94, /* PRC 32K 97 x 151 mm */ + DMPAPER_P32KBIG = 95, /* PRC 32K(Big) 97 x 151 mm */ + DMPAPER_PENV_1 = 96, /* PRC Envelope #1 102 x 165 mm */ + DMPAPER_PENV_2 = 97, /* PRC Envelope #2 102 x 176 mm */ + DMPAPER_PENV_3 = 98, /* PRC Envelope #3 125 x 176 mm */ + DMPAPER_PENV_4 = 99, /* PRC Envelope #4 110 x 208 mm */ + DMPAPER_PENV_5 = 100, /* PRC Envelope #5 110 x 220 mm */ + DMPAPER_PENV_6 = 101, /* PRC Envelope #6 120 x 230 mm */ + DMPAPER_PENV_7 = 102, /* PRC Envelope #7 160 x 230 mm */ + DMPAPER_PENV_8 = 103, /* PRC Envelope #8 120 x 309 mm */ + DMPAPER_PENV_9 = 104, /* PRC Envelope #9 229 x 324 mm */ + DMPAPER_PENV_10 = 105, /* PRC Envelope #10 324 x 458 mm */ + DMPAPER_P16K_ROTATED = 106, /* PRC 16K Rotated */ + DMPAPER_P32K_ROTATED = 107, /* PRC 32K Rotated */ + DMPAPER_P32KBIG_ROTATED = 108, /* PRC 32K(Big) Rotated */ + DMPAPER_PENV_1_ROTATED = 109, /* PRC Envelope #1 Rotated 165 x 102 mm */ + DMPAPER_PENV_2_ROTATED = 110, /* PRC Envelope #2 Rotated 176 x 102 mm */ + DMPAPER_PENV_3_ROTATED = 111, /* PRC Envelope #3 Rotated 176 x 125 mm */ + DMPAPER_PENV_4_ROTATED = 112, /* PRC Envelope #4 Rotated 208 x 110 mm */ + DMPAPER_PENV_5_ROTATED = 113, /* PRC Envelope #5 Rotated 220 x 110 mm */ + DMPAPER_PENV_6_ROTATED = 114, /* PRC Envelope #6 Rotated 230 x 120 mm */ + DMPAPER_PENV_7_ROTATED = 115, /* PRC Envelope #7 Rotated 230 x 160 mm */ + DMPAPER_PENV_8_ROTATED = 116, /* PRC Envelope #8 Rotated 309 x 120 mm */ + DMPAPER_PENV_9_ROTATED = 117, /* PRC Envelope #9 Rotated 324 x 229 mm */ + DMPAPER_PENV_10_ROTATED = 118, /* PRC Envelope #10 Rotated 458 x 324 mm */ + + DMPAPER_LAST = DMPAPER_PENV_10_ROTATED, + + DMBIN_UPPER = 1, + DMBIN_LOWER = 2, + DMBIN_MIDDLE = 3, + DMBIN_MANUAL = 4, + DMBIN_ENVELOPE = 5, + DMBIN_ENVMANUAL = 6, + DMBIN_AUTO = 7, + DMBIN_TRACTOR = 8, + DMBIN_SMALLFMT = 9, + DMBIN_LARGEFMT = 10, + DMBIN_LARGECAPACITY = 11, + DMBIN_CASSETTE = 14, + DMBIN_FORMSOURCE = 15, + DMBIN_LAST = 15, + DMBIN_USER = 256, + DMRES_DRAFT = -1, + DMRES_LOW = -2, + DMRES_MEDIUM = -3, + DMRES_HIGH = -4, + DMCOLOR_MONOCHROME = 1, + DMCOLOR_COLOR = 2, + DMDUP_SIMPLEX = 1, + DMDUP_VERTICAL = 2, + DMDUP_HORIZONTAL = 3, + + DMCOLLATE_FALSE = 0, + DMCOLLATE_TRUE = 1, + PRINTER_ENUM_LOCAL = 0x00000002, + PRINTER_ENUM_CONNECTIONS = 0x00000004, + SM_CXICON = 11, + SM_CYICON = 12, + DEFAULT_CHARSET = 1; + + public const int ERROR_ACCESS_DENIED = 5; + public const int ERROR_INVALID_PARAMETER = 87; + public const int ERROR_PROC_NOT_FOUND = 127; + public const int ERROR_CANCELLED = 1223; + + [StructLayout(LayoutKind.Sequential)] + public struct ENHMETAHEADER + { + /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. + /// Extreme care should be taken if changing the layout of the corresponding managed + /// structures to minimize the risk of buffer overruns. The affected managed classes + /// are the following: ENHMETAHEADER, MetaHeader, MetafileHeaderWmf, MetafileHeaderEmf. + public int iType; + public int nSize; + // rclBounds was a by-value RECTL structure + public int rclBounds_left; + public int rclBounds_top; + public int rclBounds_right; + public int rclBounds_bottom; + // rclFrame was a by-value RECTL structure + public int rclFrame_left; + public int rclFrame_top; + public int rclFrame_right; + public int rclFrame_bottom; + public int dSignature; + public int nVersion; + public int nBytes; + public int nRecords; + public short nHandles; + public short sReserved; + public int nDescription; + public int offDescription; + public int nPalEntries; + // szlDevice was a by-value SIZE structure + public int szlDevice_cx; + public int szlDevice_cy; + // szlMillimeters was a by-value SIZE structure + public int szlMillimeters_cx; + public int szlMillimeters_cy; + public int cbPixelFormat; + public int offPixelFormat; + public int bOpenGL; + } + + // https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513 + // https://devblogs.microsoft.com/oldnewthing/20120720-00/?p=7083 + + // Needs to be packed to 2 to get ICONDIRENTRY to follow immediately after idCount. + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public struct ICONDIR + { + // Must be 0 + public ushort idReserved; + // Must be 1 + public ushort idType; + // Count of entries + public ushort idCount; + // First entry (anysize array) + public ICONDIRENTRY idEntries; + } + + [StructLayout(LayoutKind.Sequential)] + public struct ICONDIRENTRY + { + // Width and height are 1 - 255 or 0 for 256 + public byte bWidth; + public byte bHeight; + public byte bColorCount; + public byte bReserved; + public ushort wPlanes; + public ushort wBitCount; + public uint dwBytesInRes; + public uint dwImageOffset; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Common.cs b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Common.cs new file mode 100644 index 00000000000..b79be50f044 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.Common.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + // Raw function imports for gdiplus + internal static partial class SafeNativeMethods + { + [StructLayout(LayoutKind.Sequential)] + internal struct StartupInputEx + { + public int GdiplusVersion; // Must be 1 or 2 + + public IntPtr DebugEventCallback; + + public Interop.BOOL SuppressBackgroundThread; // FALSE unless you're prepared to call + // the hook/unhook functions properly + + public Interop.BOOL SuppressExternalCodecs; // FALSE unless you want GDI+ only to use + // its internal image codecs. + public int StartupParameters; + + public static StartupInputEx GetDefault() + { + OperatingSystem os = Environment.OSVersion; + StartupInputEx result = default; + + // In Windows 7 GDI+1.1 story is different as there are different binaries per GDI+ version. + bool isWindows7 = os.Platform == PlatformID.Win32NT && os.Version.Major == 6 && os.Version.Minor == 1; + result.GdiplusVersion = isWindows7 ? 1 : 2; + result.SuppressBackgroundThread = Interop.BOOL.FALSE; + result.SuppressExternalCodecs = Interop.BOOL.FALSE; + result.StartupParameters = 0; + return result; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct StartupOutput + { + // The following 2 fields won't be used. They were originally intended + // for getting GDI+ to run on our thread - however there are marshalling + // dealing with function *'s and what not - so we make explicit calls + // to gdi+ after the fact, via the GdiplusNotificationHook and + // GdiplusNotificationUnhook methods. + public IntPtr hook; //not used + public IntPtr unhook; //not used. + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.DllImport.cs b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.DllImport.cs new file mode 100644 index 00000000000..6b13da9d0a7 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.DllImport.cs @@ -0,0 +1,1726 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + // Raw function imports for gdiplus + // Functions are loaded manually in order to accomodate different shared library names on Unix. + internal static partial class SafeNativeMethods + { + internal static unsafe partial class Gdip + { + private const string LibraryName = "gdiplus.dll"; + + private static void PlatformInitialize() + { + } + + // Imported functions + [DllImport(LibraryName, ExactSpelling = true)] + private static extern int GdiplusStartup(out IntPtr token, in StartupInputEx input, out StartupOutput output); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePath(int brushMode, out IntPtr path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePath2(PointF* points, byte* types, int count, int brushMode, out IntPtr path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePath2I(Point* points, byte* types, int count, int brushMode, out IntPtr path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipClonePath(HandleRef path, out IntPtr clonepath); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeletePath(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetPath(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPointCount(HandleRef path, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathTypes(HandleRef path, byte[] types, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathPoints(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathFillMode(HandleRef path, out FillMode fillmode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathFillMode(HandleRef path, FillMode fillmode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathData(HandleRef path, GpPathData* pathData); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipStartPathFigure(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipClosePathFigure(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipClosePathFigures(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathMarker(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipClearPathMarkers(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipReversePath(HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathLastPoint(HandleRef path, out PointF lastPoint); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathLine(HandleRef path, float x1, float y1, float x2, float y2); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathLine2(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathArc(HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathBezier(HandleRef path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathBeziers(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurve(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurve2(HandleRef path, PointF* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurve3(HandleRef path, PointF* points, int count, int offset, int numberOfSegments, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathClosedCurve(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathClosedCurve2(HandleRef path, PointF* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathRectangle(HandleRef path, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathRectangles(HandleRef path, RectangleF* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathEllipse(HandleRef path, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathPie(HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathPolygon(HandleRef path, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathPath(HandleRef path, HandleRef addingPath, bool connect); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipAddPathString(HandleRef path, string s, int length, HandleRef fontFamily, int style, float emSize, ref RectangleF layoutRect, HandleRef format); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipAddPathStringI(HandleRef path, string s, int length, HandleRef fontFamily, int style, float emSize, ref Rectangle layoutRect, HandleRef format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathLineI(HandleRef path, int x1, int y1, int x2, int y2); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathLine2I(HandleRef path, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathArcI(HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathBezierI(HandleRef path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathBeziersI(HandleRef path, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurveI(HandleRef path, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurve2I(HandleRef path, Point* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathCurve3I(HandleRef path, Point* points, int count, int offset, int numberOfSegments, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathClosedCurveI(HandleRef path, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathClosedCurve2I(HandleRef path, Point* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathRectangleI(HandleRef path, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathRectanglesI(HandleRef path, Rectangle* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathEllipseI(HandleRef path, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathPieI(HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipAddPathPolygonI(HandleRef path, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipFlattenPath(HandleRef path, HandleRef matrixfloat, float flatness); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipWidenPath(HandleRef path, HandleRef pen, HandleRef matrix, float flatness); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipWarpPath(HandleRef path, HandleRef matrix, PointF* points, int count, float srcX, float srcY, float srcWidth, float srcHeight, WarpMode warpMode, float flatness); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformPath(HandleRef path, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathWorldBounds(HandleRef path, out RectangleF gprectf, HandleRef matrix, HandleRef pen); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisiblePathPoint(HandleRef path, float x, float y, HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisiblePathPointI(HandleRef path, int x, int y, HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsOutlineVisiblePathPoint(HandleRef path, float x, float y, HandleRef pen, HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsOutlineVisiblePathPointI(HandleRef path, int x, int y, HandleRef pen, HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteBrush(HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneImage(HandleRef image, out IntPtr cloneimage); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipSaveImageToFile(HandleRef image, string filename, ref Guid classId, HandleRef encoderParams); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSaveImageToStream(HandleRef image, IntPtr stream, Guid* classId, HandleRef encoderParams); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSaveAdd(HandleRef image, HandleRef encoderParams); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSaveAddImage(HandleRef image, HandleRef newImage, HandleRef encoderParams); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageGraphicsContext(HandleRef image, out IntPtr graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageBounds(HandleRef image, out RectangleF gprectf, out GraphicsUnit unit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageThumbnail(HandleRef image, int thumbWidth, int thumbHeight, out IntPtr thumbImage, Image.GetThumbnailImageAbort? callback, IntPtr callbackdata); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImagePalette(HandleRef image, IntPtr palette, int size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImagePalette(HandleRef image, IntPtr palette); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImagePaletteSize(HandleRef image, out int size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageForceValidation(IntPtr image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateFromHDC2(IntPtr hdc, IntPtr hdevice, out IntPtr graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteGraphics(HandleRef graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipReleaseDC(HandleRef graphics, IntPtr hdc); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetNearestColor(HandleRef graphics, ref int color); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern IntPtr GdipCreateHalftonePalette(); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawBeziers(HandleRef graphics, HandleRef pen, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawBeziersI(HandleRef graphics, HandleRef pen, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillPath(HandleRef graphics, HandleRef brush, HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestPoint(HandleRef graphics, HandleRef metafile, ref PointF destPoint, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestPointI(HandleRef graphics, HandleRef metafile, ref Point destPoint, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestRect(HandleRef graphics, HandleRef metafile, ref RectangleF destRect, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestRectI(HandleRef graphics, HandleRef metafile, ref Rectangle destRect, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestPoints(HandleRef graphics, HandleRef metafile, PointF* destPoints, int count, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileDestPointsI(HandleRef graphics, HandleRef metafile, Point* destPoints, int count, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestPoint(HandleRef graphics, HandleRef metafile, ref PointF destPoint, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestPointI(HandleRef graphics, HandleRef metafile, ref Point destPoint, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestRect(HandleRef graphics, HandleRef metafile, ref RectangleF destRect, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestRectI(HandleRef graphics, HandleRef metafile, ref Rectangle destRect, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestPoints(HandleRef graphics, HandleRef metafile, PointF* destPoints, int count, ref RectangleF srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEnumerateMetafileSrcRectDestPointsI(HandleRef graphics, HandleRef metafile, Point* destPoints, int count, ref Rectangle srcRect, GraphicsUnit pageUnit, Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, HandleRef imageattributes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRestoreGraphics(HandleRef graphics, int state); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetMetafileHeaderFromWmf(IntPtr hMetafile, WmfPlaceableFileHeader wmfplaceable, [In] [Out] MetafileHeaderWmf metafileHeaderWmf); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetMetafileHeaderFromEmf(IntPtr hEnhMetafile, [In] [Out] MetafileHeaderEmf metafileHeaderEmf); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetMetafileHeaderFromMetafile(HandleRef metafile, IntPtr header); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetHemfFromMetafile(HandleRef metafile, out IntPtr hEnhMetafile); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipComment(HandleRef graphics, int sizeData, byte[] data); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); + + // Shared function imports (all platforms) + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBeginContainer(HandleRef graphics, ref RectangleF dstRect, ref RectangleF srcRect, GraphicsUnit unit, out int state); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBeginContainer2(HandleRef graphics, out int state); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBeginContainerI(HandleRef graphics, ref Rectangle dstRect, ref Rectangle srcRect, GraphicsUnit unit, out int state); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipEndContainer(HandleRef graphics, int state); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateAdjustableArrowCap(float height, float width, bool isFilled, out IntPtr adjustableArrowCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAdjustableArrowCapHeight(HandleRef adjustableArrowCap, out float height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetAdjustableArrowCapHeight(HandleRef adjustableArrowCap, float height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetAdjustableArrowCapWidth(HandleRef adjustableArrowCap, float width); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAdjustableArrowCapWidth(HandleRef adjustableArrowCap, out float width); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetAdjustableArrowCapMiddleInset(HandleRef adjustableArrowCap, float middleInset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAdjustableArrowCapMiddleInset(HandleRef adjustableArrowCap, out float middleInset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetAdjustableArrowCapFillState(HandleRef adjustableArrowCap, bool fillState); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAdjustableArrowCapFillState(HandleRef adjustableArrowCap, out bool fillState); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapType(IntPtr customCap, out CustomLineCapType capType); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateCustomLineCap(HandleRef fillpath, HandleRef strokepath, LineCap baseCap, float baseInset, out IntPtr customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteCustomLineCap(IntPtr customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteCustomLineCap(HandleRef customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneCustomLineCap(HandleRef customCap, out IntPtr clonedCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCustomLineCapStrokeCaps(HandleRef customCap, LineCap startCap, LineCap endCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapStrokeCaps(HandleRef customCap, out LineCap startCap, out LineCap endCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCustomLineCapStrokeJoin(HandleRef customCap, LineJoin lineJoin); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapStrokeJoin(HandleRef customCap, out LineJoin lineJoin); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCustomLineCapBaseCap(HandleRef customCap, LineCap baseCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapBaseCap(HandleRef customCap, out LineCap baseCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCustomLineCapBaseInset(HandleRef customCap, float inset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapBaseInset(HandleRef customCap, out float inset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCustomLineCapWidthScale(HandleRef customCap, float widthScale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCustomLineCapWidthScale(HandleRef customCap, out float widthScale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePathIter(out IntPtr pathIter, HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeletePathIter(HandleRef pathIter); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterNextSubpath(HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex, out bool isClosed); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterNextSubpathPath(HandleRef pathIter, out int resultCount, HandleRef path, out bool isClosed); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterNextPathType(HandleRef pathIter, out int resultCount, out byte pathType, out int startIndex, out int endIndex); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterNextMarker(HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterNextMarkerPath(HandleRef pathIter, out int resultCount, HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterGetCount(HandleRef pathIter, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterGetSubpathCount(HandleRef pathIter, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterHasCurve(HandleRef pathIter, out bool hasCurve); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterRewind(HandleRef pathIter); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterEnumerate(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPathIterCopyData(HandleRef pathIter, out int resultCount, PointF* points, byte* types, int startIndex, int endIndex); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateHatchBrush(int hatchstyle, int forecol, int backcol, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetHatchStyle(HandleRef brush, out int hatchstyle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetHatchForegroundColor(HandleRef brush, out int forecol); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetHatchBackgroundColor(HandleRef brush, out int backcol); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneBrush(HandleRef brush, out IntPtr clonebrush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrush(ref PointF point1, ref PointF point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrushI(ref Point point1, ref Point point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrushFromRect(ref RectangleF rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrushFromRectI(ref Rectangle rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrushFromRectWithAngle(ref RectangleF rect, int color1, int color2, float angle, bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateLineBrushFromRectWithAngleI(ref Rectangle rect, int color1, int color2, float angle, bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineColors(HandleRef brush, int color1, int color2); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineColors(HandleRef brush, int[] colors); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineRect(HandleRef brush, out RectangleF gprectf); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineGammaCorrection(HandleRef brush, out bool useGammaCorrection); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineGammaCorrection(HandleRef brush, bool useGammaCorrection); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineSigmaBlend(HandleRef brush, float focus, float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineLinearBlend(HandleRef brush, float focus, float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineBlendCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLinePresetBlendCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLinePresetBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLinePresetBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineWrapMode(HandleRef brush, int wrapMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineWrapMode(HandleRef brush, out int wrapMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetLineTransform(HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyLineTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetLineTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateLineTransform(HandleRef brush, float dx, float dy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScaleLineTransform(HandleRef brush, float sx, float sy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotateLineTransform(HandleRef brush, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePathGradient(PointF* points, int count, WrapMode wrapMode, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePathGradientI(Point* points, int count, WrapMode wrapMode, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePathGradientFromPath(HandleRef path, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientCenterColor(HandleRef brush, out int color); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientCenterColor(HandleRef brush, int color); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientSurroundColorsWithCount(HandleRef brush, int[] color, ref int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientSurroundColorsWithCount(HandleRef brush, int[] argb, ref int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientCenterPoint(HandleRef brush, out PointF point); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientCenterPoint(HandleRef brush, ref PointF point); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientRect(HandleRef brush, out RectangleF gprectf); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientPointCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientSurroundColorCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientBlendCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientBlend(HandleRef brush, float[] blend, float[] positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientBlend(HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientPresetBlendCount(HandleRef brush, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientPresetBlend(HandleRef brush, int[] blend, float[] positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientPresetBlend(HandleRef brush, int[] blend, float[] positions, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientSigmaBlend(HandleRef brush, float focus, float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientLinearBlend(HandleRef brush, float focus, float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientWrapMode(HandleRef brush, int wrapmode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientWrapMode(HandleRef brush, out int wrapmode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetPathGradientTransform(HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyPathGradientTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslatePathGradientTransform(HandleRef brush, float dx, float dy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScalePathGradientTransform(HandleRef brush, float sx, float sy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotatePathGradientTransform(HandleRef brush, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPathGradientFocusScales(HandleRef brush, float[] xScale, float[] yScale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPathGradientFocusScales(HandleRef brush, float xScale, float yScale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateImageAttributes(out IntPtr imageattr); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneImageAttributes(HandleRef imageattr, out IntPtr cloneImageattr); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDisposeImageAttributes(HandleRef imageattr); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesColorMatrix(HandleRef imageattr, ColorAdjustType type, bool enableFlag, ColorMatrix? colorMatrix, ColorMatrix? grayMatrix, ColorMatrixFlag flags); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesThreshold(HandleRef imageattr, ColorAdjustType type, bool enableFlag, float threshold); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesGamma(HandleRef imageattr, ColorAdjustType type, bool enableFlag, float gamma); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesNoOp(HandleRef imageattr, ColorAdjustType type, bool enableFlag); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesColorKeys(HandleRef imageattr, ColorAdjustType type, bool enableFlag, int colorLow, int colorHigh); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesOutputChannel(HandleRef imageattr, ColorAdjustType type, bool enableFlag, ColorChannelFlag flags); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipSetImageAttributesOutputChannelColorProfile(HandleRef imageattr, ColorAdjustType type, bool enableFlag, string colorProfileFilename); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesRemapTable(HandleRef imageattr, ColorAdjustType type, bool enableFlag, int mapSize, IntPtr map); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetImageAttributesWrapMode(HandleRef imageattr, int wrapmode, int argb, bool clamp); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageAttributesAdjustedPalette(HandleRef imageattr, IntPtr palette, ColorAdjustType type); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageDecodersSize(out int numDecoders, out int size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageDecoders(int numDecoders, int size, IntPtr decoders); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageEncodersSize(out int numEncoders, out int size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageEncoders(int numEncoders, int size, IntPtr encoders); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateSolidFill(int color, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetSolidFillColor(HandleRef brush, int color); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetSolidFillColor(HandleRef brush, out int color); + + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateTexture(HandleRef bitmap, int wrapmode, out IntPtr texture); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateTexture2(HandleRef bitmap, int wrapmode, float x, float y, float width, float height, out IntPtr texture); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateTextureIA(HandleRef bitmap, HandleRef imageAttrib, float x, float y, float width, float height, out IntPtr texture); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateTexture2I(HandleRef bitmap, int wrapmode, int x, int y, int width, int height, out IntPtr texture); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateTextureIAI(HandleRef bitmap, HandleRef imageAttrib, int x, int y, int width, int height, out IntPtr texture); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetTextureTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetTextureTransform(HandleRef brush, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetTextureTransform(HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyTextureTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateTextureTransform(HandleRef brush, float dx, float dy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScaleTextureTransform(HandleRef brush, float sx, float sy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotateTextureTransform(HandleRef brush, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetTextureWrapMode(HandleRef brush, int wrapMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetTextureWrapMode(HandleRef brush, out int wrapMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetTextureImage(HandleRef brush, out IntPtr image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontCollectionFamilyCount(HandleRef fontCollection, out int numFound); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontCollectionFamilyList(HandleRef fontCollection, int numSought, IntPtr[] gpfamilies, out int numFound); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneFontFamily(IntPtr fontfamily, out IntPtr clonefontfamily); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipCreateFontFamilyFromName(string name, HandleRef fontCollection, out IntPtr FontFamily); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetGenericFontFamilySansSerif(out IntPtr fontfamily); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetGenericFontFamilySerif(out IntPtr fontfamily); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetGenericFontFamilyMonospace(out IntPtr fontfamily); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteFontFamily(HandleRef fontFamily); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipGetFamilyName(HandleRef family, char* name, int language); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsStyleAvailable(HandleRef family, FontStyle style, out int isStyleAvailable); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetEmHeight(HandleRef family, FontStyle style, out int EmHeight); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCellAscent(HandleRef family, FontStyle style, out int CellAscent); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCellDescent(HandleRef family, FontStyle style, out int CellDescent); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLineSpacing(HandleRef family, FontStyle style, out int LineSpaceing); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipNewInstalledFontCollection(out IntPtr fontCollection); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipNewPrivateFontCollection(out IntPtr fontCollection); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeletePrivateFontCollection(ref IntPtr fontCollection); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipPrivateAddFontFile(HandleRef fontCollection, string filename); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPrivateAddMemoryFont(HandleRef fontCollection, IntPtr memory, int length); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateFont(HandleRef fontFamily, float emSize, FontStyle style, GraphicsUnit unit, out IntPtr font); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateFontFromDC(IntPtr hdc, ref IntPtr font); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneFont(HandleRef font, out IntPtr cloneFont); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteFont(HandleRef font); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFamily(HandleRef font, out IntPtr family); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontStyle(HandleRef font, out FontStyle style); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontSize(HandleRef font, out float size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontHeight(HandleRef font, HandleRef graphics, out float size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontHeightGivenDPI(HandleRef font, float dpi, out float size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetFontUnit(HandleRef font, out GraphicsUnit unit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetLogFontW(HandleRef font, HandleRef graphics, ref Interop.User32.LOGFONT lf); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreatePen2(HandleRef brush, float width, int unit, out IntPtr pen); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipClonePen(HandleRef pen, out IntPtr clonepen); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeletePen(HandleRef Pen); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenMode(HandleRef pen, PenAlignment penAlign); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenMode(HandleRef pen, out PenAlignment penAlign); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenWidth(HandleRef pen, float width); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenWidth(HandleRef pen, float[] width); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenLineCap197819(HandleRef pen, int startCap, int endCap, int dashCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenStartCap(HandleRef pen, int startCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenEndCap(HandleRef pen, int endCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenStartCap(HandleRef pen, out int startCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenEndCap(HandleRef pen, out int endCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenDashCap197819(HandleRef pen, out int dashCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenDashCap197819(HandleRef pen, int dashCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenLineJoin(HandleRef pen, int lineJoin); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenLineJoin(HandleRef pen, out int lineJoin); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenCustomStartCap(HandleRef pen, HandleRef customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenCustomStartCap(HandleRef pen, out IntPtr customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenCustomEndCap(HandleRef pen, HandleRef customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenCustomEndCap(HandleRef pen, out IntPtr customCap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenMiterLimit(HandleRef pen, float miterLimit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenMiterLimit(HandleRef pen, float[] miterLimit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenTransform(HandleRef pen, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenTransform(HandleRef pen, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetPenTransform(HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyPenTransform(HandleRef brush, HandleRef matrix, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslatePenTransform(HandleRef brush, float dx, float dy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScalePenTransform(HandleRef brush, float sx, float sy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotatePenTransform(HandleRef brush, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenColor(HandleRef pen, int argb); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenColor(HandleRef pen, out int argb); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenBrushFill(HandleRef pen, HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenBrushFill(HandleRef pen, out IntPtr brush); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenFillType(HandleRef pen, out int pentype); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenDashStyle(HandleRef pen, out int dashstyle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenDashStyle(HandleRef pen, int dashstyle); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenDashArray(HandleRef pen, HandleRef memorydash, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenDashOffset(HandleRef pen, float[] dashoffset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenDashOffset(HandleRef pen, float dashoffset); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenDashCount(HandleRef pen, out int dashcount); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenDashArray(HandleRef pen, float[] memorydash, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenCompoundCount(HandleRef pen, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPenCompoundArray(HandleRef pen, float[] array, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPenCompoundArray(HandleRef pen, float[] array, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetWorldTransform(HandleRef graphics, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetWorldTransform(HandleRef graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyWorldTransform(HandleRef graphics, HandleRef matrix, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateWorldTransform(HandleRef graphics, float dx, float dy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScaleWorldTransform(HandleRef graphics, float sx, float sy, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotateWorldTransform(HandleRef graphics, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetWorldTransform(HandleRef graphics, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCompositingMode(HandleRef graphics, CompositingMode compositingMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetTextRenderingHint(HandleRef graphics, TextRenderingHint textRenderingHint); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetTextContrast(HandleRef graphics, int textContrast); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetInterpolationMode(HandleRef graphics, InterpolationMode interpolationMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCompositingMode(HandleRef graphics, out CompositingMode compositingMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetRenderingOrigin(HandleRef graphics, int x, int y); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRenderingOrigin(HandleRef graphics, out int x, out int y); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetCompositingQuality(HandleRef graphics, CompositingQuality quality); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetCompositingQuality(HandleRef graphics, out CompositingQuality quality); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetSmoothingMode(HandleRef graphics, SmoothingMode smoothingMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetSmoothingMode(HandleRef graphics, out SmoothingMode smoothingMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPixelOffsetMode(HandleRef graphics, PixelOffsetMode pixelOffsetMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPixelOffsetMode(HandleRef graphics, out PixelOffsetMode pixelOffsetMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetTextRenderingHint(HandleRef graphics, out TextRenderingHint textRenderingHint); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetTextContrast(HandleRef graphics, out int textContrast); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetInterpolationMode(HandleRef graphics, out InterpolationMode interpolationMode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPageUnit(HandleRef graphics, out GraphicsUnit unit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPageScale(HandleRef graphics, out float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPageUnit(HandleRef graphics, GraphicsUnit unit); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPageScale(HandleRef graphics, float scale); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetDpiX(HandleRef graphics, out float dpi); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetDpiY(HandleRef graphics, out float dpi); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMatrix(out IntPtr matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMatrix2(float m11, float m12, float m21, float m22, float dx, float dy, out IntPtr matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMatrix3(ref RectangleF rect, PointF* dstplg, out IntPtr matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMatrix3I(ref Rectangle rect, Point* dstplg, out IntPtr matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneMatrix(HandleRef matrix, out IntPtr cloneMatrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteMatrix(HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetMatrixElements(HandleRef matrix, float m11, float m12, float m21, float m22, float dx, float dy); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipMultiplyMatrix(HandleRef matrix, HandleRef matrix2, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateMatrix(HandleRef matrix, float offsetX, float offsetY, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipScaleMatrix(HandleRef matrix, float scaleX, float scaleY, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRotateMatrix(HandleRef matrix, float angle, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipShearMatrix(HandleRef matrix, float shearX, float shearY, MatrixOrder order); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipInvertMatrix(HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformMatrixPoints(HandleRef matrix, PointF* pts, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformMatrixPointsI(HandleRef matrix, Point* pts, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipVectorTransformMatrixPoints(HandleRef matrix, PointF* pts, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipVectorTransformMatrixPointsI(HandleRef matrix, Point* pts, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern unsafe int GdipGetMatrixElements(HandleRef matrix, float* m); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsMatrixInvertible(HandleRef matrix, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsMatrixIdentity(HandleRef matrix, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsMatrixEqual(HandleRef matrix, HandleRef matrix2, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegion(out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegionRect(ref RectangleF gprectf, out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegionRectI(ref Rectangle gprect, out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegionPath(HandleRef path, out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegionRgnData(byte[] rgndata, int size, out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateRegionHrgn(IntPtr hRgn, out IntPtr region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneRegion(HandleRef region, out IntPtr cloneregion); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteRegion(HandleRef region); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillRegion(HandleRef graphics, HandleRef brush, HandleRef region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetInfinite(HandleRef region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetEmpty(HandleRef region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCombineRegionRect(HandleRef region, ref RectangleF gprectf, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCombineRegionRectI(HandleRef region, ref Rectangle gprect, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCombineRegionPath(HandleRef region, HandleRef path, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCombineRegionRegion(HandleRef region, HandleRef region2, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateRegion(HandleRef region, float dx, float dy); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateRegionI(HandleRef region, int dx, int dy); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformRegion(HandleRef region, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionBounds(HandleRef region, HandleRef graphics, out RectangleF gprectf); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionHRgn(HandleRef region, HandleRef graphics, out IntPtr hrgn); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsEmptyRegion(HandleRef region, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsInfiniteRegion(HandleRef region, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsEqualRegion(HandleRef region, HandleRef region2, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionDataSize(HandleRef region, out int bufferSize); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionData(HandleRef region, byte[] regionData, int bufferSize, out int sizeFilled); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRegionPoint(HandleRef region, float X, float Y, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRegionPointI(HandleRef region, int X, int Y, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRegionRect(HandleRef region, float X, float Y, float width, float height, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRegionRectI(HandleRef region, int X, int Y, int width, int height, HandleRef graphics, out int boolean); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionScansCount(HandleRef region, out int count, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetRegionScans(HandleRef region, RectangleF* rects, out int count, HandleRef matrix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateFromHDC(IntPtr hdc, out IntPtr graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetClipGraphics(HandleRef graphics, HandleRef srcgraphics, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetClipRect(HandleRef graphics, float x, float y, float width, float height, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetClipRectI(HandleRef graphics, int x, int y, int width, int height, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetClipPath(HandleRef graphics, HandleRef path, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetClipRegion(HandleRef graphics, HandleRef region, CombineMode mode); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipResetClip(HandleRef graphics); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTranslateClip(HandleRef graphics, float dx, float dy); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetClip(HandleRef graphics, HandleRef region); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetClipBounds(HandleRef graphics, out RectangleF rect); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsClipEmpty(HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetVisibleClipBounds(HandleRef graphics, out RectangleF rect); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleClipEmpty(HandleRef graphics, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisiblePoint(HandleRef graphics, float x, float y, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisiblePointI(HandleRef graphics, int x, int y, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRect(HandleRef graphics, float x, float y, float width, float height, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipIsVisibleRectI(HandleRef graphics, int x, int y, int width, int height, out bool result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipFlush(HandleRef graphics, FlushIntention intention); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetDC(HandleRef graphics, out IntPtr hdc); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatMeasurableCharacterRanges(HandleRef format, int rangeCount, [In] [Out] CharacterRange[] range); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateStringFormat(StringFormatFlags options, int language, out IntPtr format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipStringFormatGetGenericDefault(out IntPtr format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipStringFormatGetGenericTypographic(out IntPtr format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDeleteStringFormat(HandleRef format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneStringFormat(HandleRef format, out IntPtr newFormat); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatFlags(HandleRef format, StringFormatFlags options); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatFlags(HandleRef format, out StringFormatFlags result); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatAlign(HandleRef format, StringAlignment align); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatAlign(HandleRef format, out StringAlignment align); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatLineAlign(HandleRef format, StringAlignment align); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatLineAlign(HandleRef format, out StringAlignment align); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatHotkeyPrefix(HandleRef format, HotkeyPrefix hotkeyPrefix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatHotkeyPrefix(HandleRef format, out HotkeyPrefix hotkeyPrefix); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatTabStops(HandleRef format, float firstTabOffset, int count, float[] tabStops); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatTabStops(HandleRef format, int count, out float firstTabOffset, [In] [Out] float[] tabStops); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatTabStopCount(HandleRef format, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatMeasurableCharacterRangeCount(HandleRef format, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatTrimming(HandleRef format, StringTrimming trimming); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatTrimming(HandleRef format, out StringTrimming trimming); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetStringFormatDigitSubstitution(HandleRef format, int langID, StringDigitSubstitute sds); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetStringFormatDigitSubstitution(HandleRef format, out int langID, out StringDigitSubstitute sds); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageDimension(HandleRef image, out float width, out float height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageWidth(HandleRef image, out int width); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageHeight(HandleRef image, out int height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageHorizontalResolution(HandleRef image, out float horzRes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageVerticalResolution(HandleRef image, out float vertRes); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageFlags(HandleRef image, out int flags); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageRawFormat(HandleRef image, ref Guid format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImagePixelFormat(HandleRef image, out PixelFormat format); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageGetFrameCount(HandleRef image, ref Guid dimensionID, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageSelectActiveFrame(HandleRef image, ref Guid dimensionID, int frameIndex); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageRotateFlip(HandleRef image, int rotateFlipType); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetAllPropertyItems(HandleRef image, uint totalBufferSize, uint numProperties, PropertyItemInternal* allItems); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyCount(HandleRef image, out uint numOfProperty); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyIdList(HandleRef image, uint numOfProperty, int* list); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyItem(HandleRef image, int propid, uint propSize, PropertyItemInternal* buffer); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertyItemSize(HandleRef image, int propid, out uint size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetPropertySize(HandleRef image, out uint totalBufferSize, out uint numProperties); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRemovePropertyItem(HandleRef image, int propid); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSetPropertyItem(HandleRef image, PropertyItemInternal* item); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageType(HandleRef image, out int type); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetImageType(IntPtr image, out int type); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDisposeImage(HandleRef image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipDisposeImage(IntPtr image); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipCreateBitmapFromFileICM(string filename, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, IntPtr scan0, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromGraphics(int width, int height, HandleRef graphics, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromHICON(IntPtr hicon, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateBitmapFromResource(IntPtr hresource, IntPtr name, out IntPtr bitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateHBITMAPFromBitmap(HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateHICONFromBitmap(HandleRef nativeBitmap, out IntPtr hicon); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneBitmapArea(float x, float y, float width, float height, int format, HandleRef srcbitmap, out IntPtr dstbitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCloneBitmapAreaI(int x, int y, int width, int height, int format, HandleRef srcbitmap, out IntPtr dstbitmap); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBitmapLockBits(HandleRef bitmap, ref Rectangle rect, ImageLockMode flags, PixelFormat format, [In] [Out] BitmapData lockedBitmapData); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBitmapUnlockBits(HandleRef bitmap, BitmapData lockedBitmapData); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBitmapGetPixel(HandleRef bitmap, int x, int y, out int argb); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBitmapSetPixel(HandleRef bitmap, int x, int y, int argb); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipBitmapSetResolution(HandleRef bitmap, float dpix, float dpiy); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageGetFrameDimensionsCount(HandleRef image, out int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipImageGetFrameDimensionsList(HandleRef image, Guid* dimensionIDs, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMetafileFromEmf(IntPtr hEnhMetafile, bool deleteEmf, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipCreateMetafileFromWmf(IntPtr hMetafile, bool deleteWmf, WmfPlaceableFileHeader wmfplacealbeHeader, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipCreateMetafileFromFile(string file, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileI(IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipRecordMetafileFileNameI(string fileName, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipPlayMetafileRecord(HandleRef metafile, EmfPlusRecordType recordType, int flags, int dataSize, byte[] data); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipSaveGraphics(HandleRef graphics, out int state); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawArc(HandleRef graphics, HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawArcI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawLinesI(HandleRef graphics, HandleRef pen, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawBezier(HandleRef graphics, HandleRef pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawEllipse(HandleRef graphics, HandleRef pen, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawEllipseI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawLine(HandleRef graphics, HandleRef pen, float x1, float y1, float x2, float y2); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawLineI(HandleRef graphics, HandleRef pen, int x1, int y1, int x2, int y2); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawLines(HandleRef graphics, HandleRef pen, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawPath(HandleRef graphics, HandleRef pen, HandleRef path); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawPie(HandleRef graphics, HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawPieI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawPolygon(HandleRef graphics, HandleRef pen, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawPolygonI(HandleRef graphics, HandleRef pen, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillEllipse(HandleRef graphics, HandleRef brush, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillEllipseI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillPolygon(HandleRef graphics, HandleRef brush, PointF* points, int count, FillMode brushMode); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillPolygonI(HandleRef graphics, HandleRef brush, Point* points, int count, FillMode brushMode); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillRectangle(HandleRef graphics, HandleRef brush, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillRectangleI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillRectangles(HandleRef graphics, HandleRef brush, RectangleF* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillRectanglesI(HandleRef graphics, HandleRef brush, Rectangle* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int GdipDrawString(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, HandleRef brush); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImageRectI(HandleRef graphics, HandleRef image, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGraphicsClear(HandleRef graphics, int argb); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawClosedCurve(HandleRef graphics, HandleRef pen, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawClosedCurveI(HandleRef graphics, HandleRef pen, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawClosedCurve2(HandleRef graphics, HandleRef pen, PointF* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawClosedCurve2I(HandleRef graphics, HandleRef pen, Point* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurve(HandleRef graphics, HandleRef pen, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurveI(HandleRef graphics, HandleRef pen, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurve2(HandleRef graphics, HandleRef pen, PointF* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurve2I(HandleRef graphics, HandleRef pen, Point* points, int count, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurve3(HandleRef graphics, HandleRef pen, PointF* points, int count, int offset, int numberOfSegments, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawCurve3I(HandleRef graphics, HandleRef pen, Point* points, int count, int offset, int numberOfSegments, float tension); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillClosedCurve(HandleRef graphics, HandleRef brush, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillClosedCurveI(HandleRef graphics, HandleRef brush, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillClosedCurve2(HandleRef graphics, HandleRef brush, PointF* points, int count, float tension, FillMode mode); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillClosedCurve2I(HandleRef graphics, HandleRef brush, Point* points, int count, float tension, FillMode mode); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillPie(HandleRef graphics, HandleRef brush, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipFillPieI(HandleRef graphics, HandleRef brush, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipMeasureString(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, ref RectangleF boundingBox, out int codepointsFitted, out int linesFilled); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipMeasureCharacterRanges(HandleRef graphics, string textString, int length, HandleRef font, ref RectangleF layoutRect, HandleRef stringFormat, int characterCount, [In] [Out] IntPtr[] region); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImageI(HandleRef graphics, HandleRef image, int x, int y); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImage(HandleRef graphics, HandleRef image, float x, float y); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePoints(HandleRef graphics, HandleRef image, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePointsI(HandleRef graphics, HandleRef image, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImageRectRectI(HandleRef graphics, HandleRef image, int dstx, int dsty, int dstwidth, int dstheight, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePointsRect(HandleRef graphics, HandleRef image, PointF* points, int count, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImageRectRect(HandleRef graphics, HandleRef image, float dstx, float dsty, float dstwidth, float dstheight, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePointsRectI(HandleRef graphics, HandleRef image, Point* points, int count, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, HandleRef imageAttributes, Graphics.DrawImageAbort? callback, HandleRef callbackdata); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImageRect(HandleRef graphics, HandleRef image, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePointRect(HandleRef graphics, HandleRef image, float x, float y, float srcx, float srcy, float srcwidth, float srcheight, int srcunit); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawImagePointRectI(HandleRef graphics, HandleRef image, int x, int y, int srcx, int srcy, int srcwidth, int srcheight, int srcunit); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawRectangle(HandleRef graphics, HandleRef pen, float x, float y, float width, float height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawRectangleI(HandleRef graphics, HandleRef pen, int x, int y, int width, int height); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawRectangles(HandleRef graphics, HandleRef pen, RectangleF* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true, SetLastError = true)] + internal static extern int GdipDrawRectanglesI(HandleRef graphics, HandleRef pen, Rectangle* rects, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformPoints(HandleRef graphics, int destSpace, int srcSpace, PointF* points, int count); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipTransformPointsI(HandleRef graphics, int destSpace, int srcSpace, Point* points, int count); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipLoadImageFromFileICM(string filename, out IntPtr image); + + [DllImport(LibraryName, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetEncoderParameterListSize(HandleRef image, ref Guid encoder, out int size); + + [DllImport(LibraryName, ExactSpelling = true)] + internal static extern int GdipGetEncoderParameterList(HandleRef image, ref Guid encoder, int size, IntPtr buffer); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs new file mode 100644 index 00000000000..eecc42d94ab --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/GdiplusNative.cs @@ -0,0 +1,4460 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing +{ + // Raw function imports for gdiplus + internal static partial class SafeNativeMethods + { + internal static unsafe partial class Gdip + { + private const string LibraryName = "gdiplus.dll"; + + // Imported functions + [LibraryImport(LibraryName)] + private static partial int GdiplusStartup(out IntPtr token, in StartupInputEx input, out StartupOutput output); + + [LibraryImport(LibraryName)] + internal static partial int GdipBeginContainer( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, ref RectangleF dstRect, ref RectangleF srcRect, GraphicsUnit unit, out int state); + + [LibraryImport(LibraryName)] + internal static partial int GdipBeginContainer2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int state); + + [LibraryImport(LibraryName)] + internal static partial int GdipBeginContainerI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, ref Rectangle dstRect, ref Rectangle srcRect, GraphicsUnit unit, out int state); + + [LibraryImport(LibraryName)] + internal static partial int GdipEndContainer( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int state); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateAdjustableArrowCap(float height, float width, [MarshalAs(UnmanagedType.Bool)] bool isFilled, out IntPtr adjustableArrowCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetAdjustableArrowCapHeight( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, out float height); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetAdjustableArrowCapHeight( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, float height); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetAdjustableArrowCapWidth( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, float width); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetAdjustableArrowCapWidth( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, out float width); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetAdjustableArrowCapMiddleInset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, float middleInset); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetAdjustableArrowCapMiddleInset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, out float middleInset); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetAdjustableArrowCapFillState( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, [MarshalAs(UnmanagedType.Bool)] bool fillState); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetAdjustableArrowCapFillState( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef adjustableArrowCap, [MarshalAs(UnmanagedType.Bool)] out bool fillState); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapType(IntPtr customCap, out CustomLineCapType capType); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateCustomLineCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fillpath, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef strokepath, LineCap baseCap, float baseInset, out IntPtr customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteCustomLineCap(IntPtr customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteCustomLineCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneCustomLineCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out IntPtr clonedCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCustomLineCapStrokeCaps( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, LineCap startCap, LineCap endCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapStrokeCaps( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out LineCap startCap, out LineCap endCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCustomLineCapStrokeJoin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, LineJoin lineJoin); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapStrokeJoin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out LineJoin lineJoin); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCustomLineCapBaseCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, LineCap baseCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapBaseCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out LineCap baseCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCustomLineCapBaseInset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, float inset); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapBaseInset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out float inset); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCustomLineCapWidthScale( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, float widthScale); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCustomLineCapWidthScale( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap, out float widthScale); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePathIter(out IntPtr pathIter, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeletePathIter( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterNextSubpath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex, [MarshalAs(UnmanagedType.Bool)] out bool isClosed); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterNextSubpathPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, [MarshalAs(UnmanagedType.Bool)] out bool isClosed); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterNextPathType( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, out byte pathType, out int startIndex, out int endIndex); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterNextMarker( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, out int startIndex, out int endIndex); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterNextMarkerPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterGetCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterGetSubpathCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterHasCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, [MarshalAs(UnmanagedType.Bool)] out bool hasCurve); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterRewind( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterEnumerate( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, PointF* points, byte* types, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipPathIterCopyData( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pathIter, out int resultCount, PointF* points, byte* types, int startIndex, int endIndex); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateHatchBrush(int hatchstyle, int forecol, int backcol, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetHatchStyle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int hatchstyle); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetHatchForegroundColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int forecol); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetHatchBackgroundColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int backcol); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneBrush( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out IntPtr clonebrush); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrush(ref PointF point1, ref PointF point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrushI(ref Point point1, ref Point point2, int color1, int color2, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrushFromRect(ref RectangleF rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrushFromRectI(ref Rectangle rect, int color1, int color2, LinearGradientMode lineGradientMode, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrushFromRectWithAngle(ref RectangleF rect, int color1, int color2, float angle, [MarshalAs(UnmanagedType.Bool)] bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateLineBrushFromRectWithAngleI(ref Rectangle rect, int color1, int color2, float angle, [MarshalAs(UnmanagedType.Bool)] bool isAngleScaleable, WrapMode wrapMode, out IntPtr lineGradient); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineColors( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int color1, int color2); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineColors( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int[] colors); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out RectangleF gprectf); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineGammaCorrection( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, [MarshalAs(UnmanagedType.Bool)] out bool useGammaCorrection); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineGammaCorrection( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, [MarshalAs(UnmanagedType.Bool)] bool useGammaCorrection); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineSigmaBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float focus, float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineLinearBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float focus, float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineBlendCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLinePresetBlendCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLinePresetBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLinePresetBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int wrapMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int wrapMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float dx, float dy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScaleLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float sx, float sy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotateLineTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePathGradient(PointF* points, int count, WrapMode wrapMode, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePathGradientI(Point* points, int count, WrapMode wrapMode, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePathGradientFromPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientCenterColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int color); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientCenterColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int color); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientSurroundColorsWithCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int[] color, ref int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientSurroundColorsWithCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int[] argb, ref int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientCenterPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out PointF point); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientCenterPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, ref PointF point); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out RectangleF gprectf); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientPointCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientSurroundColorCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientBlendCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float[] blend, float[] positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, IntPtr blend, IntPtr positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientPresetBlendCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientPresetBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int[] blend, float[] positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientPresetBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int[] blend, float[] positions, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientSigmaBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float focus, float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientLinearBlend( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float focus, float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int wrapmode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int wrapmode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetPathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyPathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslatePathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float dx, float dy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScalePathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float sx, float sy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotatePathGradientTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathGradientFocusScales( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float[] xScale, float[] yScale); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathGradientFocusScales( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float xScale, float yScale); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateImageAttributes(out IntPtr imageattr); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneImageAttributes( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, out IntPtr cloneImageattr); + + [LibraryImport(LibraryName)] + internal static partial int GdipDisposeImageAttributes( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesColorMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(ColorMatrix.PinningMarshaller))] +#endif + ColorMatrix? colorMatrix, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(ColorMatrix.PinningMarshaller))] +#endif + ColorMatrix? grayMatrix, ColorMatrixFlag flags); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesThreshold( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, float threshold); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesGamma( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, float gamma); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesNoOp( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesColorKeys( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, int colorLow, int colorHigh); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesOutputChannel( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, ColorChannelFlag flags); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipSetImageAttributesOutputChannelColorProfile( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, string colorProfileFilename); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesRemapTable( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, ColorAdjustType type, [MarshalAs(UnmanagedType.Bool)] bool enableFlag, int mapSize, IntPtr map); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImageAttributesWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, int wrapmode, int argb, [MarshalAs(UnmanagedType.Bool)] bool clamp); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageAttributesAdjustedPalette( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattr, IntPtr palette, ColorAdjustType type); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageDecodersSize(out int numDecoders, out int size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageDecoders(int numDecoders, int size, IntPtr decoders); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageEncodersSize(out int numEncoders, out int size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageEncoders(int numEncoders, int size, IntPtr encoders); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateSolidFill(int color, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetSolidFillColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int color); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetSolidFillColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int color); + + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateTexture( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, int wrapmode, out IntPtr texture); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateTexture2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, int wrapmode, float x, float y, float width, float height, out IntPtr texture); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateTextureIA( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttrib, float x, float y, float width, float height, out IntPtr texture); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateTexture2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, int wrapmode, int x, int y, int width, int height, out IntPtr texture); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateTextureIAI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttrib, int x, int y, int width, int height, out IntPtr texture); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float dx, float dy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScaleTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float sx, float sy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotateTextureTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetTextureWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int wrapMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetTextureWrapMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out int wrapMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetTextureImage( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, out IntPtr image); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontCollectionFamilyCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontCollection, out int numFound); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontCollectionFamilyList( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontCollection, int numSought, IntPtr[] gpfamilies, out int numFound); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneFontFamily(IntPtr fontfamily, out IntPtr clonefontfamily); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipCreateFontFamilyFromName(string name, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontCollection, out IntPtr FontFamily); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilySansSerif(out IntPtr fontfamily); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilySerif(out IntPtr fontfamily); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetGenericFontFamilyMonospace(out IntPtr fontfamily); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteFontFamily( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontFamily); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipGetFamilyName( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, char* name, int language); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsStyleAvailable( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, FontStyle style, out int isStyleAvailable); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetEmHeight( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, FontStyle style, out int EmHeight); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCellAscent( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, FontStyle style, out int CellAscent); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCellDescent( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, FontStyle style, out int CellDescent); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLineSpacing( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef family, FontStyle style, out int LineSpaceing); + + [LibraryImport(LibraryName)] + internal static partial int GdipNewInstalledFontCollection(out IntPtr fontCollection); + + [LibraryImport(LibraryName)] + internal static partial int GdipNewPrivateFontCollection(out IntPtr fontCollection); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeletePrivateFontCollection(ref IntPtr fontCollection); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipPrivateAddFontFile( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontCollection, string filename); + + [LibraryImport(LibraryName)] + internal static partial int GdipPrivateAddMemoryFont( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontCollection, IntPtr memory, int length); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateFont( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontFamily, float emSize, FontStyle style, GraphicsUnit unit, out IntPtr font); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateFontFromDC(IntPtr hdc, ref IntPtr font); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneFont( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, out IntPtr cloneFont); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteFont( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFamily( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, out IntPtr family); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontStyle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, out FontStyle style); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontSize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, out float size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontHeight( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out float size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontHeightGivenDPI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, float dpi, out float size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetFontUnit( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, out GraphicsUnit unit); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetLogFontW( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, ref Interop.User32.LOGFONT lf); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePen2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float width, int unit, out IntPtr pen); + + [LibraryImport(LibraryName)] + internal static partial int GdipClonePen( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out IntPtr clonepen); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeletePen( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef Pen); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PenAlignment penAlign); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out PenAlignment penAlign); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenWidth( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float width); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenWidth( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] width); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenLineCap197819( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int startCap, int endCap, int dashCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenStartCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int startCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenEndCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int endCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenStartCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int startCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenEndCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int endCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenDashCap197819( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int dashCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenDashCap197819( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int dashCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenLineJoin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int lineJoin); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenLineJoin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int lineJoin); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenCustomStartCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenCustomStartCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out IntPtr customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenCustomEndCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenCustomEndCap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out IntPtr customCap); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenMiterLimit( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float miterLimit); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenMiterLimit( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] miterLimit); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetPenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyPenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslatePenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float dx, float dy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScalePenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float sx, float sy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotatePenTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int argb); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int argb); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenBrushFill( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenBrushFill( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out IntPtr brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenFillType( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int pentype); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenDashStyle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int dashstyle); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenDashStyle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int dashstyle); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenDashArray( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef memorydash, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenDashOffset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] dashoffset); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenDashOffset( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float dashoffset); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenDashCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int dashcount); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenDashArray( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] memorydash, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenCompoundCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPenCompoundArray( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] array, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPenCompoundArray( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float[] array, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float dx, float dy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScaleWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float sx, float sy, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotateWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetWorldTransform( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCompositingMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, CompositingMode compositingMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetTextRenderingHint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, TextRenderingHint textRenderingHint); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetTextContrast( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int textContrast); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetInterpolationMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, InterpolationMode interpolationMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCompositingMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out CompositingMode compositingMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetRenderingOrigin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int x, int y); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRenderingOrigin( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int x, out int y); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetCompositingQuality( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, CompositingQuality quality); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetCompositingQuality( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out CompositingQuality quality); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetSmoothingMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, SmoothingMode smoothingMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetSmoothingMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out SmoothingMode smoothingMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPixelOffsetMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, PixelOffsetMode pixelOffsetMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPixelOffsetMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out PixelOffsetMode pixelOffsetMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetTextRenderingHint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out TextRenderingHint textRenderingHint); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetTextContrast( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int textContrast); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetInterpolationMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out InterpolationMode interpolationMode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPageUnit( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out GraphicsUnit unit); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPageScale( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPageUnit( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, GraphicsUnit unit); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPageScale( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float scale); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetDpiX( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out float dpi); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetDpiY( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out float dpi); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMatrix(out IntPtr matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMatrix2(float m11, float m12, float m21, float m22, float dx, float dy, out IntPtr matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMatrix3(ref RectangleF rect, PointF* dstplg, out IntPtr matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMatrix3I(ref Rectangle rect, Point* dstplg, out IntPtr matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, out IntPtr cloneMatrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetMatrixElements( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float m11, float m12, float m21, float m22, float dx, float dy); + + [LibraryImport(LibraryName)] + internal static partial int GdipMultiplyMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix2, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float offsetX, float offsetY, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipScaleMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float scaleX, float scaleY, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipRotateMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float angle, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipShearMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float shearX, float shearY, MatrixOrder order); + + [LibraryImport(LibraryName)] + internal static partial int GdipInvertMatrix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformMatrixPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, PointF* pts, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformMatrixPointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, Point* pts, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipVectorTransformMatrixPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, PointF* pts, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipVectorTransformMatrixPointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, Point* pts, int count); + + [LibraryImport(LibraryName)] + internal static unsafe partial int GdipGetMatrixElements( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float* m); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsMatrixInvertible( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsMatrixIdentity( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsMatrixEqual( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix2, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegion(out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegionRect(ref RectangleF gprectf, out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegionRectI(ref Rectangle gprect, out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegionPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegionRgnData(byte[] rgndata, int size, out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateRegionHrgn(IntPtr hRgn, out IntPtr region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, out IntPtr cloneregion); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetInfinite( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetEmpty( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region); + + [LibraryImport(LibraryName)] + internal static partial int GdipCombineRegionRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, ref RectangleF gprectf, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipCombineRegionRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, ref Rectangle gprect, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipCombineRegionPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipCombineRegionRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region2, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, float dx, float dy); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateRegionI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, int dx, int dy); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionBounds( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out RectangleF gprectf); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionHRgn( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out IntPtr hrgn); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsEmptyRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsInfiniteRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsEqualRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region2, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionDataSize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, out int bufferSize); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionData( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, byte[] regionData, int bufferSize, out int sizeFilled); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRegionPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, float X, float Y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRegionPointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, int X, int Y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRegionRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, float X, float Y, float width, float height, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRegionRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, int X, int Y, int width, int height, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int boolean); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionScansCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, out int count, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetRegionScans( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, RectangleF* rects, out int count, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateFromHDC(IntPtr hdc, out IntPtr graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetClipGraphics( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef srcgraphics, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetClipRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float x, float y, float width, float height, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetClipRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int x, int y, int width, int height, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetClipPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetClipRegion( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region, CombineMode mode); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetClip( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipTranslateClip( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float dx, float dy); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetClip( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef region); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetClipBounds( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out RectangleF rect); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsClipEmpty( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetVisibleClipBounds( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out RectangleF rect); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleClipEmpty( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisiblePoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float x, float y, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisiblePointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int x, int y, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, float x, float y, float width, float height, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisibleRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int x, int y, int width, int height, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipFlush( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, FlushIntention intention); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetDC( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out IntPtr hdc); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatMeasurableCharacterRanges( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, int rangeCount, CharacterRange[] range); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateStringFormat(StringFormatFlags options, int language, out IntPtr format); + + [LibraryImport(LibraryName)] + internal static partial int GdipStringFormatGetGenericDefault(out IntPtr format); + + [LibraryImport(LibraryName)] + internal static partial int GdipStringFormatGetGenericTypographic(out IntPtr format); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteStringFormat( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneStringFormat( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out IntPtr newFormat); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatFlags( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, StringFormatFlags options); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatFlags( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out StringFormatFlags result); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatAlign( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, StringAlignment align); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatAlign( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out StringAlignment align); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatLineAlign( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, StringAlignment align); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatLineAlign( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out StringAlignment align); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatHotkeyPrefix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, HotkeyPrefix hotkeyPrefix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatHotkeyPrefix( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out HotkeyPrefix hotkeyPrefix); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatTabStops( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, float firstTabOffset, int count, float[] tabStops); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatTabStops( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, int count, out float firstTabOffset, float[] tabStops); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatTabStopCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatMeasurableCharacterRangeCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatTrimming( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, StringTrimming trimming); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatTrimming( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out StringTrimming trimming); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetStringFormatDigitSubstitution( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, int langID, StringDigitSubstitute sds); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetStringFormatDigitSubstitution( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format, out int langID, out StringDigitSubstitute sds); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageDimension( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out float width, out float height); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageWidth( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int width); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageHeight( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int height); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageHorizontalResolution( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out float horzRes); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageVerticalResolution( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out float vertRes); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageFlags( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int flags); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageRawFormat( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, ref Guid format); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImagePixelFormat( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out PixelFormat format); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageGetFrameCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, ref Guid dimensionID, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageSelectActiveFrame( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, ref Guid dimensionID, int frameIndex); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageRotateFlip( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int rotateFlipType); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetAllPropertyItems( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, uint totalBufferSize, uint numProperties, PropertyItemInternal* allItems); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPropertyCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out uint numOfProperty); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPropertyIdList( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, uint numOfProperty, int* list); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPropertyItem( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int propid, uint propSize, PropertyItemInternal* buffer); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPropertyItemSize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int propid, out uint size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPropertySize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out uint totalBufferSize, out uint numProperties); + + [LibraryImport(LibraryName)] + internal static partial int GdipRemovePropertyItem( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int propid); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPropertyItem( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, PropertyItemInternal* item); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageType( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int type); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageType(IntPtr image, out int type); + + [LibraryImport(LibraryName)] + internal static partial int GdipDisposeImage( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image); + + [LibraryImport(LibraryName)] + internal static partial int GdipDisposeImage(IntPtr image); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipCreateBitmapFromFile(string filename, out IntPtr bitmap); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipCreateBitmapFromFileICM(string filename, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, IntPtr scan0, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromGraphics(int width, int height, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromHBITMAP(IntPtr hbitmap, IntPtr hpalette, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromHICON(IntPtr hicon, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromResource(IntPtr hresource, IntPtr name, out IntPtr bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateHBITMAPFromBitmap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef nativeBitmap, out IntPtr hbitmap, int argbBackground); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateHICONFromBitmap( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef nativeBitmap, out IntPtr hicon); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneBitmapArea(float x, float y, float width, float height, int format, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef srcbitmap, out IntPtr dstbitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneBitmapAreaI(int x, int y, int width, int height, int format, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef srcbitmap, out IntPtr dstbitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipBitmapLockBits( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, ref Rectangle rect, ImageLockMode flags, PixelFormat format, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(BitmapData.PinningMarshaller))] +#endif + BitmapData lockedBitmapData); + + [LibraryImport(LibraryName)] + internal static partial int GdipBitmapUnlockBits( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(BitmapData.PinningMarshaller))] +#endif + BitmapData lockedBitmapData); + + [LibraryImport(LibraryName)] + internal static partial int GdipBitmapGetPixel( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, int x, int y, out int argb); + + [LibraryImport(LibraryName)] + internal static partial int GdipBitmapSetPixel( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, int x, int y, int argb); + + [LibraryImport(LibraryName)] + internal static partial int GdipBitmapSetResolution( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef bitmap, float dpix, float dpiy); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageGetFrameDimensionsCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageGetFrameDimensionsList( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, Guid* dimensionIDs, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMetafileFromEmf(IntPtr hEnhMetafile, [MarshalAs(UnmanagedType.Bool)] bool deleteEmf, out IntPtr metafile); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMetafileFromWmf(IntPtr hMetafile, [MarshalAs(UnmanagedType.Bool)] bool deleteWmf, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(WmfPlaceableFileHeader.PinningMarshaller))] +#endif + WmfPlaceableFileHeader wmfplacealbeHeader, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipCreateMetafileFromFile(string file, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafile(IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileI(IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, ref RectangleF frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileFileName(string fileName, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileFileNameI(string fileName, IntPtr referenceHdc, EmfType emfType, ref Rectangle frameRect, MetafileFrameUnit frameUnit, string? description, out IntPtr metafile); + + [LibraryImport(LibraryName)] + internal static partial int GdipPlayMetafileRecord( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, EmfPlusRecordType recordType, int flags, int dataSize, byte[] data); + + [LibraryImport(LibraryName)] + internal static partial int GdipSaveGraphics( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, out int state); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawArc( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawArcI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawLinesI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawBezier( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawEllipse( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x, float y, float width, float height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawEllipseI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int x, int y, int width, int height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawLine( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x1, float y1, float x2, float y2); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawLineI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int x1, int y1, int x2, int y2); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawLines( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawPie( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawPieI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawPolygon( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawPolygonI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillEllipse( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float x, float y, float width, float height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillEllipseI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int x, int y, int width, int height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillPolygon( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, PointF* points, int count, FillMode brushMode); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillPolygonI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, Point* points, int count, FillMode brushMode); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillRectangle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float x, float y, float width, float height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillRectangleI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int x, int y, int width, int height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillRectangles( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, RectangleF* rects, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillRectanglesI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, Rectangle* rects, int count); + + [LibraryImport(LibraryName, SetLastError = true, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipDrawString( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, string textString, int length, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, ref RectangleF layoutRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef stringFormat, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImageRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int x, int y, int width, int height); + + [LibraryImport(LibraryName)] + internal static partial int GdipGraphicsClear( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int argb); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawClosedCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawClosedCurveI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawClosedCurve2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawClosedCurve2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurveI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurve2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurve2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurve3( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count, int offset, int numberOfSegments, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawCurve3I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count, int offset, int numberOfSegments, float tension); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillClosedCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillClosedCurveI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillClosedCurve2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, PointF* points, int count, float tension, FillMode mode); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillClosedCurve2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, Point* points, int count, float tension, FillMode mode); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillPie( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillPieI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipMeasureString( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, string textString, int length, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, ref RectangleF layoutRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef stringFormat, ref RectangleF boundingBox, out int codepointsFitted, out int linesFilled); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipMeasureCharacterRanges( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, string textString, int length, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef font, ref RectangleF layoutRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef stringFormat, int characterCount, IntPtr[] region); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImageI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int x, int y); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImage( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, float x, float y); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImageRectRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int dstx, int dsty, int dstwidth, int dstheight, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttributes, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.DrawImageAbortMarshaller))] +#endif + Graphics.DrawImageAbort? callback, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef callbackdata); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePointsRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, PointF* points, int count, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttributes, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.DrawImageAbortMarshaller))] +#endif + Graphics.DrawImageAbort? callback, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef callbackdata); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImageRectRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, float dstx, float dsty, float dstwidth, float dstheight, float srcx, float srcy, float srcwidth, float srcheight, GraphicsUnit srcunit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttributes, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.DrawImageAbortMarshaller))] +#endif + Graphics.DrawImageAbort? callback, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef callbackdata); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePointsRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, Point* points, int count, int srcx, int srcy, int srcwidth, int srcheight, GraphicsUnit srcunit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageAttributes, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.DrawImageAbortMarshaller))] +#endif + Graphics.DrawImageAbort? callback, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef callbackdata); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImageRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, float x, float y, float width, float height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePointRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, float x, float y, float srcx, float srcy, float srcwidth, float srcheight, int srcunit); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawImagePointRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int x, int y, int srcx, int srcy, int srcwidth, int srcheight, int srcunit); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawRectangle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, float x, float y, float width, float height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawRectangleI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, int x, int y, int width, int height); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawRectangles( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, RectangleF* rects, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawRectanglesI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Rectangle* rects, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int destSpace, int srcSpace, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformPointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int destSpace, int srcSpace, Point* points, int count); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipLoadImageFromFileICM(string filename, out IntPtr image); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipLoadImageFromFile(string filename, out IntPtr image); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetEncoderParameterListSize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, ref Guid encoder, out int size); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetEncoderParameterList( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, ref Guid encoder, int size, IntPtr buffer); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePath(int brushMode, out IntPtr path); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePath2(PointF* points, byte* types, int count, int brushMode, out IntPtr path); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreatePath2I(Point* points, byte* types, int count, int brushMode, out IntPtr path); + + [LibraryImport(LibraryName)] + internal static partial int GdipClonePath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out IntPtr clonepath); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeletePath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipResetPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPointCount( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathTypes( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, byte[] types, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathFillMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out FillMode fillmode); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathFillMode( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, FillMode fillmode); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathData( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, GpPathData* pathData); + + [LibraryImport(LibraryName)] + internal static partial int GdipStartPathFigure( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipClosePathFigure( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipClosePathFigures( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetPathMarker( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipClearPathMarkers( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipReversePath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathLastPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out PointF lastPoint); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathLine( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x1, float y1, float x2, float y2); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathLine2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathArc( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathBezier( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathBeziers( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurve2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurve3( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count, int offset, int numberOfSegments, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathClosedCurve( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathClosedCurve2( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathRectangle( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, float width, float height); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathRectangles( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, RectangleF* rects, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathEllipse( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, float width, float height); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathPie( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, float width, float height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathPolygon( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, PointF* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef addingPath, [MarshalAs(UnmanagedType.Bool)] bool connect); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipAddPathString( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, string s, int length, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontFamily, int style, float emSize, ref RectangleF layoutRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipAddPathStringI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, string s, int length, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef fontFamily, int style, float emSize, ref Rectangle layoutRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef format); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathLineI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x1, int y1, int x2, int y2); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathLine2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathArcI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathBezierI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathBeziersI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurveI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurve2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathCurve3I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count, int offset, int numberOfSegments, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathClosedCurveI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathClosedCurve2I( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count, float tension); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathRectangleI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, int width, int height); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathRectanglesI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Rectangle* rects, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathEllipseI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, int width, int height); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathPieI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, int width, int height, float startAngle, float sweepAngle); + + [LibraryImport(LibraryName)] + internal static partial int GdipAddPathPolygonI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, Point* points, int count); + + [LibraryImport(LibraryName)] + internal static partial int GdipFlattenPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrixfloat, float flatness); + + [LibraryImport(LibraryName)] + internal static partial int GdipWidenPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, float flatness); + + [LibraryImport(LibraryName)] + internal static partial int GdipWarpPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, PointF* points, int count, float srcX, float srcY, float srcWidth, float srcHeight, WarpMode warpMode, float flatness); + + [LibraryImport(LibraryName)] + internal static partial int GdipTransformPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetPathWorldBounds( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, out RectangleF gprectf, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef matrix, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisiblePathPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsVisiblePathPointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsOutlineVisiblePathPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, float x, float y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipIsOutlineVisiblePathPointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path, int x, int y, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, [MarshalAs(UnmanagedType.Bool)] out bool result); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteBrush( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush); + + [LibraryImport(LibraryName)] + internal static partial int GdipLoadImageFromStream(IntPtr stream, IntPtr* image); + + [LibraryImport(LibraryName)] + internal static partial int GdipLoadImageFromStreamICM(IntPtr stream, IntPtr* image); + + [LibraryImport(LibraryName)] + internal static partial int GdipCloneImage( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out IntPtr cloneimage); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipSaveImageToFile( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, string filename, ref Guid classId, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef encoderParams); + + [LibraryImport(LibraryName)] + internal static partial int GdipSaveImageToStream( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, IntPtr stream, Guid* classId, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef encoderParams); + + [LibraryImport(LibraryName)] + internal static partial int GdipSaveAdd( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef encoderParams); + + [LibraryImport(LibraryName)] + internal static partial int GdipSaveAddImage( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef newImage, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef encoderParams); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageGraphicsContext( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out IntPtr graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageBounds( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out RectangleF gprectf, out GraphicsUnit unit); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImageThumbnail( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, int thumbWidth, int thumbHeight, out IntPtr thumbImage, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Image.GetThumbnailImageAbortMarshaller))] +#endif + Image.GetThumbnailImageAbort? callback, IntPtr callbackdata); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImagePalette( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, IntPtr palette, int size); + + [LibraryImport(LibraryName)] + internal static partial int GdipSetImagePalette( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, IntPtr palette); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetImagePaletteSize( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef image, out int size); + + [LibraryImport(LibraryName)] + internal static partial int GdipImageForceValidation(IntPtr image); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateFromHDC2(IntPtr hdc, IntPtr hdevice, out IntPtr graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipDeleteGraphics( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics); + + [LibraryImport(LibraryName)] + internal static partial int GdipReleaseDC( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, IntPtr hdc); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetNearestColor( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, ref int color); + + [LibraryImport(LibraryName)] + internal static partial IntPtr GdipCreateHalftonePalette(); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawBeziers( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, PointF* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipDrawBeziersI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef pen, Point* points, int count); + + [LibraryImport(LibraryName, SetLastError = true)] + internal static partial int GdipFillPath( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef brush, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef path); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref PointF destPoint, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestPointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref Point destPoint, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref RectangleF destRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref Rectangle destRect, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, PointF* destPoints, int count, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileDestPointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, Point* destPoints, int count, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestPoint( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref PointF destPoint, ref RectangleF srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestPointI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref Point destPoint, ref Rectangle srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestRect( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref RectangleF destRect, ref RectangleF srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestRectI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, ref Rectangle destRect, ref Rectangle srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestPoints( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, PointF* destPoints, int count, ref RectangleF srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipEnumerateMetafileSrcRectDestPointsI( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, Point* destPoints, int count, ref Rectangle srcRect, GraphicsUnit pageUnit, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(Graphics.EnumerateMetafileProcMarshaller))] +#endif + Graphics.EnumerateMetafileProc callback, IntPtr callbackdata, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef imageattributes); + + [LibraryImport(LibraryName)] + internal static partial int GdipRestoreGraphics( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int state); + + [LibraryImport(LibraryName, EntryPoint = "GdipGetMetafileHeaderFromWmf")] + private static partial int GdipGetMetafileHeaderFromWmf_Internal(IntPtr hMetafile, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(WmfPlaceableFileHeader.PinningMarshaller))] +#endif + WmfPlaceableFileHeader wmfplaceable, +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(MetafileHeaderWmf.Marshaller))] + ref MetafileHeaderWmf metafileHeaderWmf +#else + MetafileHeaderWmf metafileHeaderWmf +#endif + ); + + internal static int GdipGetMetafileHeaderFromWmf(IntPtr hMetafile, + WmfPlaceableFileHeader wmfplaceable, + MetafileHeaderWmf metafileHeaderWmf + ) + { + return GdipGetMetafileHeaderFromWmf_Internal(hMetafile, + wmfplaceable, +#if NET7_0_OR_GREATER + ref +#endif + metafileHeaderWmf + ); + } + + [LibraryImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromEmf(IntPtr hEnhMetafile, MetafileHeaderEmf metafileHeaderEmf); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipGetMetafileHeaderFromFile(string filename, IntPtr header); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromStream(IntPtr stream, IntPtr header); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetMetafileHeaderFromMetafile( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, IntPtr header); + + [LibraryImport(LibraryName)] + internal static partial int GdipGetHemfFromMetafile( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef metafile, out IntPtr hEnhMetafile); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateMetafileFromStream(IntPtr stream, IntPtr* metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, RectangleF* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileStream(IntPtr stream, IntPtr referenceHdc, EmfType emfType, IntPtr pframeRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipRecordMetafileStreamI(IntPtr stream, IntPtr referenceHdc, EmfType emfType, Rectangle* frameRect, MetafileFrameUnit frameUnit, string? description, IntPtr* metafile); + + [LibraryImport(LibraryName)] + internal static partial int GdipComment( +#if NET7_0_OR_GREATER + [MarshalUsing(typeof(HandleRefMarshaller))] +#endif + HandleRef graphics, int sizeData, byte[] data); + + [LibraryImport(LibraryName, StringMarshalling = StringMarshalling.Utf16)] + internal static partial int GdipCreateFontFromLogfontW(IntPtr hdc, ref Interop.User32.LOGFONT lf, out IntPtr font); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromStream(IntPtr stream, IntPtr* bitmap); + + [LibraryImport(LibraryName)] + internal static partial int GdipCreateBitmapFromStreamICM(IntPtr stream, IntPtr* bitmap); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Graphics.cs b/src/System.Drawing.Common/src/System/Drawing/Graphics.cs new file mode 100644 index 00000000000..1ac11321818 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Graphics.cs @@ -0,0 +1,3465 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Globalization; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Diagnostics.CodeAnalysis; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Encapsulates a GDI+ drawing surface. + /// + public sealed class Graphics : MarshalByRefObject, IDisposable, IDeviceContext + { +#if FINALIZATION_WATCH + static readonly TraceSwitch GraphicsFinalization = new TraceSwitch("GraphicsFinalization", "Tracks the creation and destruction of finalization"); + internal static string GetAllocationStack() { + if (GraphicsFinalization.TraceVerbose) { + return Environment.StackTrace; + } + else { + return "Enabled 'GraphicsFinalization' switch to see stack of allocation"; + } + } + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + /// + /// The context state previous to the current Graphics context (the head of the stack). + /// We don't keep a GraphicsContext for the current context since it is available at any time from GDI+ and + /// we don't want to keep track of changes in it. + /// + private GraphicsContext? _previousContext; + + private static readonly object s_syncObject = new object(); + + // Object reference used for printing; it could point to a PrintPreviewGraphics to obtain the VisibleClipBounds, or + // a DeviceContext holding a printer DC. + private object? _printingHelper; + + // GDI+'s preferred HPALETTE. + private static IntPtr s_halftonePalette; + + // pointer back to the Image backing a specific graphic object + private Image? _backingImage; + + /// + /// Handle to native DC - obtained from the GDI+ graphics object. We need to cache it to implement + /// IDeviceContext interface. + /// + private IntPtr _nativeHdc; + + public delegate bool DrawImageAbort(IntPtr callbackdata); + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(DrawImageAbort), MarshalMode.ManagedToUnmanagedIn, typeof(KeepAliveMarshaller))] + internal static class DrawImageAbortMarshaller + { + internal unsafe struct KeepAliveMarshaller + { + private delegate Interop.BOOL DrawImageAbortNative(IntPtr callbackdata); + private DrawImageAbortNative? _managed; + private delegate* unmanaged _nativeFunction; + public void FromManaged(DrawImageAbort? managed) + { + _managed = managed is null ? null : data => managed(data) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); + } + + public delegate* unmanaged ToUnmanaged() + { + return _nativeFunction; + } + + public void OnInvoked() + { + GC.KeepAlive(_managed); + } + + public void Free() { } + } + } +#endif + + /// + /// Callback for EnumerateMetafile methods. + /// This method can then call Metafile.PlayRecord to play the record that was just enumerated. + /// + /// if >= MinRecordType, it's an EMF+ record + /// always 0 for EMF records + /// size of the data, or 0 if no data + /// pointer to the data, or NULL if no data (UINT32 aligned) + /// pointer to callbackData, if any + /// False to abort enumerating, true to continue. + public delegate bool EnumerateMetafileProc( + EmfPlusRecordType recordType, + int flags, + int dataSize, + IntPtr data, + PlayRecordCallback? callbackData); + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(EnumerateMetafileProc), MarshalMode.ManagedToUnmanagedIn, typeof(KeepAliveMarshaller))] + internal static class EnumerateMetafileProcMarshaller + { + internal unsafe struct KeepAliveMarshaller + { + private delegate Interop.BOOL EnumerateMetafileProcNative( + EmfPlusRecordType recordType, + int flags, + int dataSize, + IntPtr data, + IntPtr callbackData); + private EnumerateMetafileProcNative? _managed; + private delegate* unmanaged _nativeFunction; + public void FromManaged(EnumerateMetafileProc? managed) + { + _managed = managed is null ? null : (recordType, flags, dataSize, data, callbackData) => + managed(recordType, flags, dataSize, data, callbackData == IntPtr.Zero ? null : Marshal.GetDelegateForFunctionPointer(callbackData)) ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + _nativeFunction = _managed is null ? null : (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); + } + + public delegate* unmanaged ToUnmanaged() + { + return _nativeFunction; + } + + public void OnInvoked() + { + GC.KeepAlive(_managed); + } + + public void Free() {} + } + } +#endif + + /// + /// Constructor to initialize this object from a native GDI+ Graphics pointer. + /// + private Graphics(IntPtr gdipNativeGraphics) + { + if (gdipNativeGraphics == IntPtr.Zero) + throw new ArgumentNullException(nameof(gdipNativeGraphics)); + + NativeGraphics = gdipNativeGraphics; + } + + /// + /// Creates a new instance of the class from the specified handle to a device context. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdc(IntPtr hdc) + { + if (hdc == IntPtr.Zero) + throw new ArgumentNullException(nameof(hdc)); + + return FromHdcInternal(hdc); + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdcInternal(IntPtr hdc) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHDC(hdc, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates a new instance of the Graphics class from the specified handle to a device context and handle to a device. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHdc(IntPtr hdc, IntPtr hdevice) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHDC2(hdc, hdevice, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates a new instance of the class from a window handle. + /// + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHwnd(IntPtr hwnd) => FromHwndInternal(hwnd); + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public static Graphics FromHwndInternal(IntPtr hwnd) + { + Gdip.CheckStatus(Gdip.GdipCreateFromHWND(hwnd, out IntPtr nativeGraphics)); + return new Graphics(nativeGraphics); + } + + /// + /// Creates an instance of the class from an existing . + /// + public static Graphics FromImage(Image image) + { + ArgumentNullException.ThrowIfNull(image); + + if ((image.PixelFormat & PixelFormat.Indexed) != 0) + throw new ArgumentException(SR.GdiplusCannotCreateGraphicsFromIndexedPixelFormat, nameof(image)); + + Gdip.CheckStatus(Gdip.GdipGetImageGraphicsContext( + new HandleRef(image, image.nativeImage), + out IntPtr nativeGraphics)); + + return new Graphics(nativeGraphics) { _backingImage = image }; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public void ReleaseHdcInternal(IntPtr hdc) + { + Gdip.CheckStatus(!Gdip.Initialized ? Gdip.Ok : + Gdip.GdipReleaseDC(new HandleRef(this, NativeGraphics), hdc)); + _nativeHdc = IntPtr.Zero; + } + + /// + /// Deletes this , and frees the memory allocated for it. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { +#if DEBUG && FINALIZATION_WATCH + if (!disposing && _nativeGraphics != IntPtr.Zero) + { + Debug.WriteLine("System.Drawing.Graphics: ***************************************************"); + Debug.WriteLine("System.Drawing.Graphics: Object Disposed through finalization:\n" + allocationSite); + } +#endif + while (_previousContext != null) + { + // Dispose entire stack. + GraphicsContext? context = _previousContext.Previous; + _previousContext.Dispose(); + _previousContext = context; + } + + if (NativeGraphics != IntPtr.Zero) + { + try + { + if (_nativeHdc != IntPtr.Zero) // avoid a handle leak. + { + ReleaseHdc(); + } + + if (PrintingHelper is DeviceContext printerDC) + { + printerDC.Dispose(); + _printingHelper = null; + } + +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteGraphics(new HandleRef(this, NativeGraphics)); + +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + } + finally + { + NativeGraphics = IntPtr.Zero; + } + } + } + + ~Graphics() => Dispose(false); + + /// + /// Handle to native GDI+ graphics object. This object is created on demand. + /// + internal IntPtr NativeGraphics { get; private set; } + + public Region Clip + { + get + { + var region = new Region(); + int status = Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(region, region.NativeRegion)); + Gdip.CheckStatus(status); + + return region; + } + set => SetClip(value, CombineMode.Replace); + } + + public RectangleF ClipBounds + { + get + { + Gdip.CheckStatus(Gdip.GdipGetClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); + return rect; + } + } + + /// + /// Gets or sets the associated with this . + /// + public CompositingMode CompositingMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetCompositingMode(new HandleRef(this, NativeGraphics), out CompositingMode mode)); + return mode; + } + set + { + if (value < CompositingMode.SourceOver || value > CompositingMode.SourceCopy) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingMode)); + + Gdip.CheckStatus(Gdip.GdipSetCompositingMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public CompositingQuality CompositingQuality + { + get + { + Gdip.CheckStatus(Gdip.GdipGetCompositingQuality(new HandleRef(this, NativeGraphics), out CompositingQuality cq)); + return cq; + } + set + { + if (value < CompositingQuality.Invalid || value > CompositingQuality.AssumeLinear) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(CompositingQuality)); + + Gdip.CheckStatus(Gdip.GdipSetCompositingQuality(new HandleRef(this, NativeGraphics), value)); + } + } + + public float DpiX + { + get + { + Gdip.CheckStatus(Gdip.GdipGetDpiX(new HandleRef(this, NativeGraphics), out float dpi)); + return dpi; + } + } + + public float DpiY + { + get + { + Gdip.CheckStatus(Gdip.GdipGetDpiY(new HandleRef(this, NativeGraphics), out float dpi)); + return dpi; + } + } + + /// + /// Gets or sets the interpolation mode associated with this Graphics. + /// + public InterpolationMode InterpolationMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetInterpolationMode(new HandleRef(this, NativeGraphics), out InterpolationMode mode)); + return mode; + } + set + { + if (value < InterpolationMode.Invalid || value > InterpolationMode.HighQualityBicubic) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(InterpolationMode)); + + // GDI+ interprets the value of InterpolationMode and sets a value accordingly. + // Libgdiplus does not, so do this manually here. + switch (value) + { + case InterpolationMode.Default: + case InterpolationMode.Low: + value = InterpolationMode.Bilinear; + break; + case InterpolationMode.High: + value = InterpolationMode.HighQualityBicubic; + break; + case InterpolationMode.Invalid: + throw new ArgumentException(SR.GdiplusInvalidParameter); + default: + break; + } + + Gdip.CheckStatus(Gdip.GdipSetInterpolationMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public bool IsClipEmpty + { + get + { + Gdip.CheckStatus(Gdip.GdipIsClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); + return isEmpty; + } + } + + public bool IsVisibleClipEmpty + { + get + { + Gdip.CheckStatus(Gdip.GdipIsVisibleClipEmpty(new HandleRef(this, NativeGraphics), out bool isEmpty)); + return isEmpty; + } + } + + public float PageScale + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPageScale(new HandleRef(this, NativeGraphics), out float scale)); + return scale; + } + set + { + // Libgdiplus doesn't perform argument validation, so do this here for compatibility. + if (value <= 0 || value > 1000000032) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPageScale(new HandleRef(this, NativeGraphics), value)); + } + } + + public GraphicsUnit PageUnit + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPageUnit(new HandleRef(this, NativeGraphics), out GraphicsUnit unit)); + return unit; + } + set + { + if (value < GraphicsUnit.World || value > GraphicsUnit.Millimeter) + throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(GraphicsUnit)); + + // GDI+ doesn't allow GraphicsUnit.World as a valid value for PageUnit. + // Libgdiplus doesn't perform argument validation, so do this here. + if (value == GraphicsUnit.World) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPageUnit(new HandleRef(this, NativeGraphics), value)); + } + } + + public PixelOffsetMode PixelOffsetMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPixelOffsetMode(new HandleRef(this, NativeGraphics), out PixelOffsetMode mode)); + return mode; + } + set + { + if (value < PixelOffsetMode.Invalid || value > PixelOffsetMode.Half) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PixelOffsetMode)); + + // GDI+ doesn't allow PixelOffsetMode.Invalid as a valid value for PixelOffsetMode. + // Libgdiplus doesn't perform argument validation, so do this here. + if (value == PixelOffsetMode.Invalid) + throw new ArgumentException(SR.GdiplusInvalidParameter); + + Gdip.CheckStatus(Gdip.GdipSetPixelOffsetMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public Point RenderingOrigin + { + get + { + Gdip.CheckStatus(Gdip.GdipGetRenderingOrigin(new HandleRef(this, NativeGraphics), out int x, out int y)); + return new Point(x, y); + } + set + { + Gdip.CheckStatus(Gdip.GdipSetRenderingOrigin(new HandleRef(this, NativeGraphics), value.X, value.Y)); + } + } + + public SmoothingMode SmoothingMode + { + get + { + Gdip.CheckStatus(Gdip.GdipGetSmoothingMode(new HandleRef(this, NativeGraphics), out SmoothingMode mode)); + return mode; + } + set + { + if (value < SmoothingMode.Invalid || value > SmoothingMode.AntiAlias) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(SmoothingMode)); + + // GDI+ interprets the value of SmoothingMode and sets a value accordingly. + // Libgdiplus does not, so do this manually here. + switch (value) + { + case SmoothingMode.Default: + case SmoothingMode.HighSpeed: + value = SmoothingMode.None; + break; + case SmoothingMode.HighQuality: + value = SmoothingMode.AntiAlias; + break; + case SmoothingMode.Invalid: + throw new ArgumentException(SR.GdiplusInvalidParameter); + default: + break; + } + + Gdip.CheckStatus(Gdip.GdipSetSmoothingMode(new HandleRef(this, NativeGraphics), value)); + } + } + + public int TextContrast + { + get + { + Gdip.CheckStatus(Gdip.GdipGetTextContrast(new HandleRef(this, NativeGraphics), out int textContrast)); + return textContrast; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetTextContrast(new HandleRef(this, NativeGraphics), value)); + } + } + + /// + /// Gets or sets the rendering mode for text associated with this . + /// + public TextRenderingHint TextRenderingHint + { + get + { + Gdip.CheckStatus(Gdip.GdipGetTextRenderingHint(new HandleRef(this, NativeGraphics), out TextRenderingHint hint)); + return hint; + } + set + { + if (value < TextRenderingHint.SystemDefault || value > TextRenderingHint.ClearTypeGridFit) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(TextRenderingHint)); + + Gdip.CheckStatus(Gdip.GdipSetTextRenderingHint(new HandleRef(this, NativeGraphics), value)); + } + } + + /// + /// Gets or sets the world transform for this . + /// + public Matrix Transform + { + get + { + var matrix = new Matrix(); + Gdip.CheckStatus(Gdip.GdipGetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix))); + + return matrix; + } + set + { + Gdip.CheckStatus(Gdip.GdipSetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(value, value.NativeMatrix))); + } + } + + /// + /// Gets or sets the world transform elements for this . + /// + /// + /// This is a more performant alternative to that does not need disposal. + /// + public unsafe Matrix3x2 TransformElements + { + get + { + Gdip.CheckStatus(Gdip.GdipCreateMatrix(out IntPtr nativeMatrix)); + + try + { + Gdip.CheckStatus(Gdip.GdipGetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); + + Matrix3x2 matrix = default; + Gdip.CheckStatus(Gdip.GdipGetMatrixElements(new HandleRef(null, nativeMatrix), (float*)&matrix)); + return matrix; + } + finally + { + if (nativeMatrix != IntPtr.Zero) + { + Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); + } + } + } + set + { + IntPtr nativeMatrix = Matrix.CreateNativeHandle(value); + + try + { + Gdip.CheckStatus(Gdip.GdipSetWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(null, nativeMatrix))); + } + finally + { + if (nativeMatrix != IntPtr.Zero) + { + Gdip.GdipDeleteMatrix(new HandleRef(null, nativeMatrix)); + } + } + } + } + + public IntPtr GetHdc() + { + IntPtr hdc; + Gdip.CheckStatus(Gdip.GdipGetDC(new HandleRef(this, NativeGraphics), out hdc)); + + _nativeHdc = hdc; // need to cache the hdc to be able to release with a call to IDeviceContext.ReleaseHdc(). + return _nativeHdc; + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + public void ReleaseHdc(IntPtr hdc) => ReleaseHdcInternal(hdc); + + public void ReleaseHdc() => ReleaseHdcInternal(_nativeHdc); + + /// + /// Forces immediate execution of all operations currently on the stack. + /// + public void Flush() => Flush(FlushIntention.Flush); + + /// + /// Forces execution of all operations currently on the stack. + /// + public void Flush(FlushIntention intention) + { + Gdip.CheckStatus(Gdip.GdipFlush(new HandleRef(this, NativeGraphics), intention)); + } + + public void SetClip(Graphics g) => SetClip(g, CombineMode.Replace); + + public void SetClip(Graphics g, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipSetClipGraphics( + new HandleRef(this, NativeGraphics), + new HandleRef(g, g.NativeGraphics), + combineMode)); + } + + public void SetClip(Rectangle rect) => SetClip(rect, CombineMode.Replace); + + public void SetClip(Rectangle rect, CombineMode combineMode) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + combineMode)); + } + + public void SetClip(RectangleF rect) => SetClip(rect, CombineMode.Replace); + + public void SetClip(RectangleF rect, CombineMode combineMode) + { + Gdip.CheckStatus(Gdip.GdipSetClipRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + combineMode)); + } + + public void SetClip(GraphicsPath path) => SetClip(path, CombineMode.Replace); + + public void SetClip(GraphicsPath path, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipSetClipPath( + new HandleRef(this, NativeGraphics), + new HandleRef(path, path._nativePath), + combineMode)); + } + + public void SetClip(Region region, CombineMode combineMode) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + combineMode)); + } + + public void IntersectClip(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Intersect)); + } + + public void IntersectClip(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Intersect)); + } + + public void IntersectClip(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + CombineMode.Intersect)); + } + + public void ExcludeClip(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipSetClipRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + CombineMode.Exclude)); + } + + public void ExcludeClip(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipSetClipRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(region, region.NativeRegion), + CombineMode.Exclude)); + } + + public void ResetClip() + { + Gdip.CheckStatus(Gdip.GdipResetClip(new HandleRef(this, NativeGraphics))); + } + + public void TranslateClip(float dx, float dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); + } + + public void TranslateClip(int dx, int dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateClip(new HandleRef(this, NativeGraphics), dx, dy)); + } + + public bool IsVisible(int x, int y) => IsVisible(new Point(x, y)); + + public bool IsVisible(Point point) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePointI( + new HandleRef(this, NativeGraphics), + point.X, point.Y, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y)); + + public bool IsVisible(PointF point) + { + Gdip.CheckStatus(Gdip.GdipIsVisiblePoint( + new HandleRef(this, NativeGraphics), + point.X, point.Y, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(int x, int y, int width, int height) + { + return IsVisible(new Rectangle(x, y, width, height)); + } + + public bool IsVisible(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRectI( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + out bool isVisible)); + + return isVisible; + } + + public bool IsVisible(float x, float y, float width, float height) + { + return IsVisible(new RectangleF(x, y, width, height)); + } + + public bool IsVisible(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRect( + new HandleRef(this, NativeGraphics), + rect.X, rect.Y, rect.Width, rect.Height, + out bool isVisible)); + + return isVisible; + } + + /// + /// Resets the world transform to identity. + /// + public void ResetTransform() + { + Gdip.CheckStatus(Gdip.GdipResetWorldTransform(new HandleRef(this, NativeGraphics))); + } + + /// + /// Multiplies the that represents the world transform and . + /// + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + /// + /// Multiplies the that represents the world transform and . + /// + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws + // with the libgdiplus backend. Simulate a nop for compatibility with GDI+. + if (matrix.NativeMatrix == IntPtr.Zero) + return; + + Gdip.CheckStatus(Gdip.GdipMultiplyWorldTransform( + new HandleRef(this, NativeGraphics), new HandleRef(matrix, matrix.NativeMatrix), order)); + } + + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipTranslateWorldTransform(new HandleRef(this, NativeGraphics), dx, dy, order)); + } + + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipScaleWorldTransform(new HandleRef(this, NativeGraphics), sx, sy, order)); + } + + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + public void RotateTransform(float angle, MatrixOrder order) + { + Gdip.CheckStatus(Gdip.GdipRotateWorldTransform(new HandleRef(this, NativeGraphics), angle, order)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawArc( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, RectangleF rect, float startAngle, float sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawArcI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws an arc from the specified ellipse. + /// + public void DrawArc(Pen pen, Rectangle rect, float startAngle, float sweepAngle) + { + DrawArc(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws a cubic bezier curve defined by four ordered pairs that represent points. + /// + public void DrawBezier(Pen pen, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawBezier( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x1, y1, x2, y2, x3, y3, x4, y4)); + } + + /// + /// Draws a cubic bezier curve defined by four points. + /// + public void DrawBezier(Pen pen, PointF pt1, PointF pt2, PointF pt3, PointF pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Draws a cubic bezier curve defined by four points. + /// + public void DrawBezier(Pen pen, Point pt1, Point pt2, Point pt3, Point pt4) + { + DrawBezier(pen, pt1.X, pt1.Y, pt2.X, pt2.Y, pt3.X, pt3.Y, pt4.X, pt4.Y); + } + + /// + /// Draws the outline of a rectangle specified by . + /// + /// A Pen that determines the color, width, and style of the rectangle. + /// A Rectangle structure that represents the rectangle to draw. + public void DrawRectangle(Pen pen, RectangleF rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of a rectangle specified by . + /// + public void DrawRectangle(Pen pen, Rectangle rect) + { + DrawRectangle(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of the specified rectangle. + /// + public void DrawRectangle(Pen pen, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawRectangle( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of the specified rectangle. + /// + public void DrawRectangle(Pen pen, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawRectangleI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outlines of a series of rectangles. + /// + public unsafe void DrawRectangles(Pen pen, RectangleF[] rects) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(rects); + + fixed (RectangleF* r = rects) + { + CheckErrorStatus(Gdip.GdipDrawRectangles( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + r, rects.Length)); + } + } + + /// + /// Draws the outlines of a series of rectangles. + /// + public unsafe void DrawRectangles(Pen pen, Rectangle[] rects) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(rects); + + fixed (Rectangle* r = rects) + { + CheckErrorStatus(Gdip.GdipDrawRectanglesI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + r, rects.Length)); + } + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, RectangleF rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawEllipse( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of an ellipse specified by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, Rectangle rect) + { + DrawEllipse(pen, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Draws the outline of an ellipse defined by a bounding rectangle. + /// + public void DrawEllipse(Pen pen, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawEllipseI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height)); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, RectangleF rect, float startAngle, float sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawPie( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, Rectangle rect, float startAngle, float sweepAngle) + { + DrawPie(pen, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Draws the outline of a pie section defined by an ellipse and two radial lines. + /// + public void DrawPie(Pen pen, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawPieI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Draws the outline of a polygon defined by an array of points. + /// + public unsafe void DrawPolygon(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawPolygon( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws the outline of a polygon defined by an array of points. + /// + public unsafe void DrawPolygon(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawPolygonI( + new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws the lines and curves defined by a . + /// + public void DrawPath(Pen pen, GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(path); + + CheckErrorStatus(Gdip.GdipDrawPath( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + new HandleRef(path, path._nativePath))); + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + public void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments) + { + DrawCurve(pen, points, offset, numberOfSegments, 0.5f); + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, PointF[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve3( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + offset, + numberOfSegments, + tension)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Draws a curve defined by an array of points. + /// + public unsafe void DrawCurve(Pen pen, Point[] points, int offset, int numberOfSegments, float tension) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawCurve3I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + offset, + numberOfSegments, + tension)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, PointF[] points, float tension, FillMode fillmode) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a closed curve defined by an array of points. + /// + public unsafe void DrawClosedCurve(Pen pen, Point[] points, float tension, FillMode fillmode) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawClosedCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length, + tension)); + } + } + + /// + /// Fills the entire drawing surface with the specified color. + /// + public void Clear(Color color) + { + Gdip.CheckStatus(Gdip.GdipGraphicsClear(new HandleRef(this, NativeGraphics), color.ToArgb())); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, RectangleF rect) + { + FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillRectangle( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, Rectangle rect) + { + FillRectangle(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of a rectangle with a . + /// + public void FillRectangle(Brush brush, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillRectangleI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interiors of a series of rectangles with a . + /// + public unsafe void FillRectangles(Brush brush, RectangleF[] rects) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(rects); + + fixed (RectangleF* r = rects) + { + CheckErrorStatus(Gdip.GdipFillRectangles( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + r, rects.Length)); + } + } + + /// + /// Fills the interiors of a series of rectangles with a . + /// + public unsafe void FillRectangles(Brush brush, Rectangle[] rects) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(rects); + + fixed (Rectangle* r = rects) + { + CheckErrorStatus(Gdip.GdipFillRectanglesI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + r, rects.Length)); + } + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public void FillPolygon(Brush brush, PointF[] points) + { + FillPolygon(brush, points, FillMode.Alternate); + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public unsafe void FillPolygon(Brush brush, PointF[] points, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillPolygon( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + fillMode)); + } + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public void FillPolygon(Brush brush, Point[] points) + { + FillPolygon(brush, points, FillMode.Alternate); + } + + /// + /// Fills the interior of a polygon defined by an array of points. + /// + public unsafe void FillPolygon(Brush brush, Point[] points, FillMode fillMode) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillPolygonI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + fillMode)); + } + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, RectangleF rect) + { + FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillEllipse( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, Rectangle rect) + { + FillEllipse(brush, rect.X, rect.Y, rect.Width, rect.Height); + } + + /// + /// Fills the interior of an ellipse defined by a bounding rectangle. + /// + public void FillEllipse(Brush brush, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillEllipseI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height)); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, Rectangle rect, float startAngle, float sweepAngle) + { + FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + /// A Brush that determines the characteristics of the fill. + /// A Rectangle structure that represents the bounding rectangle that defines the ellipse from which the pie section comes. + /// Angle in degrees measured clockwise from the x-axis to the first side of the pie section. + /// Angle in degrees measured clockwise from the parameter to the second side of the pie section. + public void FillPie(Brush brush, RectangleF rect, float startAngle, float sweepAngle) + { + FillPie(brush, rect.X, rect.Y, rect.Width, rect.Height, startAngle, sweepAngle); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, float x, float y, float width, float height, float startAngle, float sweepAngle) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillPie( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Fills the interior of a pie section defined by an ellipse and two radial lines. + /// + public void FillPie(Brush brush, int x, int y, int width, int height, int startAngle, int sweepAngle) + { + ArgumentNullException.ThrowIfNull(brush); + + CheckErrorStatus(Gdip.GdipFillPieI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + x, y, width, height, + startAngle, + sweepAngle)); + } + + /// + /// Fills the interior a closed curve defined by an array of points. + /// + public unsafe void FillClosedCurve(Brush brush, PointF[] points) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length)); + } + } + + /// + /// Fills the interior of a closed curve defined by an array of points. + /// + public void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode) + { + FillClosedCurve(brush, points, fillmode, 0.5f); + } + + public unsafe void FillClosedCurve(Brush brush, PointF[] points, FillMode fillmode, float tension) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve2( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + tension, + fillmode)); + } + } + + /// + /// Fills the interior a closed curve defined by an array of points. + /// + public unsafe void FillClosedCurve(Brush brush, Point[] points) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurveI( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length)); + } + } + + public void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode) + { + FillClosedCurve(brush, points, fillmode, 0.5f); + } + + public unsafe void FillClosedCurve(Brush brush, Point[] points, FillMode fillmode, float tension) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipFillClosedCurve2I( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + p, points.Length, + tension, + fillmode)); + } + } + + /// + /// Draws a string with the specified font. + /// + public void DrawString(string? s, Font font, Brush brush, float x, float y) + { + DrawString(s, font, brush, new RectangleF(x, y, 0, 0), null); + } + + public void DrawString(string? s, Font font, Brush brush, PointF point) + { + DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), null); + } + + public void DrawString(string? s, Font font, Brush brush, float x, float y, StringFormat? format) + { + DrawString(s, font, brush, new RectangleF(x, y, 0, 0), format); + } + + public void DrawString(string? s, Font font, Brush brush, PointF point, StringFormat? format) + { + DrawString(s, font, brush, new RectangleF(point.X, point.Y, 0, 0), format); + } + + public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle) + { + DrawString(s, font, brush, layoutRectangle, null); + } + + public void DrawString(string? s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat? format) + { + ArgumentNullException.ThrowIfNull(brush); + if (string.IsNullOrEmpty(s)) + return; + ArgumentNullException.ThrowIfNull(font); + + CheckErrorStatus(Gdip.GdipDrawString( + new HandleRef(this, NativeGraphics), + s, + s.Length, + new HandleRef(font, font.NativeFont), + ref layoutRectangle, + new HandleRef(format, format?.nativeFormat ?? IntPtr.Zero), + new HandleRef(brush, brush.NativeBrush))); + } + + public SizeF MeasureString( + string? text, + Font font, + SizeF layoutArea, + StringFormat? stringFormat, + out int charactersFitted, + out int linesFilled) + { + if (string.IsNullOrEmpty(text)) + { + charactersFitted = 0; + linesFilled = 0; + return SizeF.Empty; + } + + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out charactersFitted, + out linesFilled)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font, PointF origin, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return SizeF.Empty; + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(origin.X, origin.Y, 0, 0); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out _, + out _)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font, SizeF layoutArea) => MeasureString(text, font, layoutArea, null); + + public SizeF MeasureString(string? text, Font font, SizeF layoutArea, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return SizeF.Empty; + if (font == null) + throw new ArgumentNullException(nameof(font)); + + RectangleF layout = new RectangleF(0, 0, layoutArea.Width, layoutArea.Height); + RectangleF boundingBox = default; + + Gdip.CheckStatus(Gdip.GdipMeasureString( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layout, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + ref boundingBox, + out _, + out _)); + + return boundingBox.Size; + } + + public SizeF MeasureString(string? text, Font font) + { + return MeasureString(text, font, new SizeF(0, 0)); + } + + public SizeF MeasureString(string? text, Font font, int width) + { + return MeasureString(text, font, new SizeF(width, 999999)); + } + + public SizeF MeasureString(string? text, Font font, int width, StringFormat? format) + { + return MeasureString(text, font, new SizeF(width, 999999), format); + } + + public Region[] MeasureCharacterRanges(string? text, Font font, RectangleF layoutRect, StringFormat? stringFormat) + { + if (string.IsNullOrEmpty(text)) + return Array.Empty(); + if (font == null) + throw new ArgumentNullException(nameof(font)); + + Gdip.CheckStatus(Gdip.GdipGetStringFormatMeasurableCharacterRangeCount( + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + out int count)); + + IntPtr[] gpRegions = new IntPtr[count]; + Region[] regions = new Region[count]; + + for (int f = 0; f < count; f++) + { + regions[f] = new Region(); + gpRegions[f] = regions[f].NativeRegion; + } + + Gdip.CheckStatus(Gdip.GdipMeasureCharacterRanges( + new HandleRef(this, NativeGraphics), + text, + text.Length, + new HandleRef(font, font.NativeFont), + ref layoutRect, + new HandleRef(stringFormat, stringFormat?.nativeFormat ?? IntPtr.Zero), + count, + gpRegions)); + + return regions; + } + + /// + /// Draws the specified image at the specified location. + /// + public void DrawImage(Image image, PointF point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImage(Image image, float x, float y) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImage( + new HandleRef(this, NativeGraphics), new HandleRef(image, image.nativeImage), + x, y); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, RectangleF rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } + + public void DrawImage(Image image, float x, float y, float width, float height) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + width, height); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Point point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImage(Image image, int x, int y) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Rectangle rect) + { + DrawImage(image, rect.X, rect.Y, rect.Width, rect.Height); + } + + public void DrawImage(Image image, int x, int y, int width, int height) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + width, height); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImageUnscaled(Image image, Point point) + { + DrawImage(image, point.X, point.Y); + } + + public void DrawImageUnscaled(Image image, int x, int y) + { + DrawImage(image, x, y); + } + + public void DrawImageUnscaled(Image image, Rectangle rect) + { + DrawImage(image, rect.X, rect.Y); + } + + public void DrawImageUnscaled(Image image, int x, int y, int width, int height) + { + DrawImage(image, x, y); + } + + public void DrawImageUnscaledAndClipped(Image image, Rectangle rect) + { + ArgumentNullException.ThrowIfNull(image); + + int width = Math.Min(rect.Width, image.Width); + int height = Math.Min(rect.Height, image.Height); + + // We could put centering logic here too for the case when the image + // is smaller than the rect. + DrawImage(image, rect, 0, 0, width, height, GraphicsUnit.Pixel); + } + + // Affine or perspective blt + // destPoints.Length = 3: rect => parallelogram + // destPoints[0] <=> top-left corner of the source rectangle + // destPoints[1] <=> top-right corner + // destPoints[2] <=> bottom-left corner + // destPoints.Length = 4: rect => quad + // destPoints[3] <=> bottom-right corner + // + // @notes Perspective blt only works for bitmap images. + + public unsafe void DrawImage(Image image, PointF[] destPoints) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePoints( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, count); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public unsafe void DrawImage(Image image, Point[] destPoints) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (Point* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, count); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, float x, float y, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImagePointRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + (int)srcUnit); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, int x, int y, Rectangle srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImagePointRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + x, y, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + (int)srcUnit); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, RectangleF destRect, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage(Image image, Rectangle destRect, Rectangle srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public unsafe void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + NativeMethods.NullHandleRef, + null, + NativeMethods.NullHandleRef); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, PointF[] destPoints, RectangleF srcRect, GraphicsUnit srcUnit, ImageAttributes? imageAttr) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); + } + + public void DrawImage( + Image image, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); + } + + public unsafe void DrawImage( + Image image, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback, + int callbackData) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (PointF* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, (IntPtr)callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage(Image image, Point[] destPoints, Rectangle srcRect, GraphicsUnit srcUnit) + { + DrawImage(image, destPoints, srcRect, srcUnit, null, null, 0); + } + + public void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, null, 0); + } + + public void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destPoints, srcRect, srcUnit, imageAttr, callback, 0); + } + + public unsafe void DrawImage( + Image image, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback, + int callbackData) + { + ArgumentNullException.ThrowIfNull(image); + ArgumentNullException.ThrowIfNull(destPoints); + + int count = destPoints.Length; + if (count != 3 && count != 4) + throw new ArgumentException(SR.GdiplusDestPointsInvalidLength); + + fixed (Point* p = destPoints) + { + int status = Gdip.GdipDrawImagePointsRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + p, destPoints.Length, + srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, + srcUnit, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, (IntPtr)callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttrs, callback, IntPtr.Zero); + } + + public void DrawImage( + Image image, + Rectangle destRect, + float srcX, + float srcY, + float srcWidth, + float srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback, + IntPtr callbackData) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRect( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcX, srcY, srcWidth, srcHeight, + srcUnit, + new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, null); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttr, + DrawImageAbort? callback) + { + DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, srcUnit, imageAttr, callback, IntPtr.Zero); + } + + public void DrawImage( + Image image, + Rectangle destRect, + int srcX, + int srcY, + int srcWidth, + int srcHeight, + GraphicsUnit srcUnit, + ImageAttributes? imageAttrs, + DrawImageAbort? callback, + IntPtr callbackData) + { + ArgumentNullException.ThrowIfNull(image); + + int status = Gdip.GdipDrawImageRectRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(image, image.nativeImage), + destRect.X, destRect.Y, destRect.Width, destRect.Height, + srcX, srcY, srcWidth, srcHeight, + srcUnit, + new HandleRef(imageAttrs, imageAttrs?.nativeImageAttributes ?? IntPtr.Zero), + callback, + new HandleRef(null, callbackData)); + + IgnoreMetafileErrors(image, ref status); + CheckErrorStatus(status); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, PointF pt1, PointF pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Draws a series of line segments that connect an array of points. + /// + public unsafe void DrawLines(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawLines( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, int x1, int y1, int x2, int y2) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawLineI(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, Point pt1, Point pt2) + { + DrawLine(pen, pt1.X, pt1.Y, pt2.X, pt2.Y); + } + + /// + /// Draws a series of line segments that connect an array of points. + /// + public unsafe void DrawLines(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawLinesI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); + } + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. + /// + public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize) + { + CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize); + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size. + /// + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize) + { + CopyFromScreen(sourceX, sourceY, destinationX, destinationY, blockRegionSize, CopyPixelOperation.SourceCopy); + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size + /// and specified raster operation. + /// + public void CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize, CopyPixelOperation copyPixelOperation) + { + CopyFromScreen(upperLeftSource.X, upperLeftSource.Y, upperLeftDestination.X, upperLeftDestination.Y, blockRegionSize, copyPixelOperation); + } + + public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, PointF destPoint, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Point destPoint, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, RectangleF destRect, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Rectangle destRect, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, callback, callbackData, null); + } + + public void EnumerateMetafile(Metafile metafile, PointF[] destPoints, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero, null); + } + + public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, callback, IntPtr.Zero); + } + + public void EnumerateMetafile(Metafile metafile, Point[] destPoints, EnumerateMetafileProc callback, IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoint, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destRect, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); + } + + public void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, IntPtr.Zero); + } + + public void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit srcUnit, + EnumerateMetafileProc callback, + IntPtr callbackData) + { + EnumerateMetafile(metafile, destPoints, srcRect, srcUnit, callback, callbackData, null); + } + + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (PointF* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformPoints( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); + } + } + + public unsafe void TransformPoints(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] pts) + { + ArgumentNullException.ThrowIfNull(pts); + + fixed (Point* p = pts) + { + Gdip.CheckStatus(Gdip.GdipTransformPointsI( + new HandleRef(this, NativeGraphics), + (int)destSpace, + (int)srcSpace, + p, + pts.Length)); + } + } + + /// + /// GDI+ will return a 'generic error' when we attempt to draw an Emf + /// image with width/height == 1. Here, we will hack around this by + /// resetting the errorstatus. Note that we don't do simple arg checking + /// for height || width == 1 here because transforms can be applied to + /// the Graphics object making it difficult to identify this scenario. + /// + private static void IgnoreMetafileErrors(Image image, ref int errorStatus) + { + if (errorStatus != Gdip.Ok && image.RawFormat.Equals(ImageFormat.Emf)) + errorStatus = Gdip.Ok; + } + + /// + /// Creates a Region class only if the native region is not infinite. + /// + internal Region? GetRegionIfNotInfinite() + { + Gdip.CheckStatus(Gdip.GdipCreateRegion(out IntPtr regionHandle)); + try + { + Gdip.GdipGetClip(new HandleRef(this, NativeGraphics), new HandleRef(null, regionHandle)); + Gdip.CheckStatus(Gdip.GdipIsInfiniteRegion( + new HandleRef(null, regionHandle), + new HandleRef(this, NativeGraphics), + out int isInfinite)); + + if (isInfinite != 0) + { + // Infinite + return null; + } + + Region region = new Region(regionHandle); + regionHandle = IntPtr.Zero; + return region; + } + finally + { + if (regionHandle != IntPtr.Zero) + { + Gdip.GdipDeleteRegion(new HandleRef(null, regionHandle)); + } + } + } + + /// + /// Represents an object used in connection with the printing API, it is used to hold a reference to a + /// PrintPreviewGraphics (fake graphics) or a printer DeviceContext (and maybe more in the future). + /// + internal object? PrintingHelper + { + get => _printingHelper; + set + { + Debug.Assert(_printingHelper == null, "WARNING: Overwritting the printing helper reference!"); + _printingHelper = value; + } + } + + /// + /// CopyPixels will perform a gdi "bitblt" operation to the source from the destination with the given size + /// and specified raster operation. + /// + public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation) + { + switch (copyPixelOperation) + { + case CopyPixelOperation.Blackness: + case CopyPixelOperation.NotSourceErase: + case CopyPixelOperation.NotSourceCopy: + case CopyPixelOperation.SourceErase: + case CopyPixelOperation.DestinationInvert: + case CopyPixelOperation.PatInvert: + case CopyPixelOperation.SourceInvert: + case CopyPixelOperation.SourceAnd: + case CopyPixelOperation.MergePaint: + case CopyPixelOperation.MergeCopy: + case CopyPixelOperation.SourceCopy: + case CopyPixelOperation.SourcePaint: + case CopyPixelOperation.PatCopy: + case CopyPixelOperation.PatPaint: + case CopyPixelOperation.Whiteness: + case CopyPixelOperation.CaptureBlt: + case CopyPixelOperation.NoMirrorBitmap: + break; + default: + throw new InvalidEnumArgumentException(nameof(copyPixelOperation), (int)copyPixelOperation, typeof(CopyPixelOperation)); + } + + int destWidth = blockRegionSize.Width; + int destHeight = blockRegionSize.Height; + + IntPtr screenDC = Interop.User32.GetDC(IntPtr.Zero); + try + { + IntPtr targetDC = GetHdc(); + int result = Interop.Gdi32.BitBlt( + targetDC, + destinationX, + destinationY, + destWidth, + destHeight, + screenDC, + sourceX, + sourceY, + (Interop.Gdi32.RasterOp)copyPixelOperation); + + //a zero result indicates a win32 exception has been thrown + if (result == 0) + { + throw new Win32Exception(); + } + } + finally + { + Interop.User32.ReleaseDC(IntPtr.Zero, screenDC); + ReleaseHdc(); + } + } + + public Color GetNearestColor(Color color) + { + int nearest = color.ToArgb(); + Gdip.CheckStatus(Gdip.GdipGetNearestColor(new HandleRef(this, NativeGraphics), ref nearest)); + return Color.FromArgb(nearest); + } + + /// + /// Draws a line connecting the two specified points. + /// + public void DrawLine(Pen pen, float x1, float y1, float x2, float y2) + { + ArgumentNullException.ThrowIfNull(pen); + + CheckErrorStatus(Gdip.GdipDrawLine(new HandleRef(this, NativeGraphics), new HandleRef(pen, pen.NativePen), x1, y1, x2, y2)); + } + + /// + /// Draws a series of cubic Bezier curves from an array of points. + /// + public unsafe void DrawBeziers(Pen pen, PointF[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (PointF* p = points) + { + CheckErrorStatus(Gdip.GdipDrawBeziers( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, points.Length)); + } + } + + /// + /// Draws a series of cubic Bezier curves from an array of points. + /// + public unsafe void DrawBeziers(Pen pen, Point[] points) + { + ArgumentNullException.ThrowIfNull(pen); + ArgumentNullException.ThrowIfNull(points); + + fixed (Point* p = points) + { + CheckErrorStatus(Gdip.GdipDrawBeziersI( + new HandleRef(this, NativeGraphics), + new HandleRef(pen, pen.NativePen), + p, + points.Length)); + } + } + + /// + /// Fills the interior of a path. + /// + public void FillPath(Brush brush, GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(path); + + CheckErrorStatus(Gdip.GdipFillPath( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + new HandleRef(path, path._nativePath))); + } + + /// + /// Fills the interior of a . + /// + public void FillRegion(Brush brush, Region region) + { + ArgumentNullException.ThrowIfNull(brush); + ArgumentNullException.ThrowIfNull(region); + + CheckErrorStatus(Gdip.GdipFillRegion( + new HandleRef(this, NativeGraphics), + new HandleRef(brush, brush.NativeBrush), + new HandleRef(region, region.NativeRegion))); + } + + public void DrawIcon(Icon icon, int x, int y) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + // We don't call the icon directly because we want to stay in GDI+ all the time + // to avoid alpha channel interop issues between gdi and gdi+ + // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. + DrawImage(icon.ToBitmap(), x, y); + } + else + { + icon.Draw(this, x, y); + } + } + + /// + /// Draws this image to a graphics object. The drawing command originates on the graphics + /// object, but a graphics object generally has no idea how to render a given image. So, + /// it passes the call to the actual image. This version crops the image to the given + /// dimensions and allows the user to specify a rectangle within the image to draw. + /// + public void DrawIcon(Icon icon, Rectangle targetRect) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + // We don't call the icon directly because we want to stay in GDI+ all the time + // to avoid alpha channel interop issues between gdi and gdi+ + // so we do icon.ToBitmap() and then we call DrawImage. This is probably slower. + DrawImage(icon.ToBitmap(), targetRect); + } + else + { + icon.Draw(this, targetRect); + } + } + + /// + /// Draws this image to a graphics object. The drawing command originates on the graphics + /// object, but a graphics object generally has no idea how to render a given image. So, + /// it passes the call to the actual image. This version stretches the image to the given + /// dimensions and allows the user to specify a rectangle within the image to draw. + /// + public void DrawIconUnstretched(Icon icon, Rectangle targetRect) + { + ArgumentNullException.ThrowIfNull(icon); + + if (_backingImage != null) + { + DrawImageUnscaled(icon.ToBitmap(), targetRect); + } + else + { + icon.DrawUnstretched(this, targetRect); + } + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoint( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRect( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (PointF* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPoints( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (Point* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileDestPointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public void EnumerateMetafile( + Metafile metafile, + PointF destPoint, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoint( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Point destPoint, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destPoint, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + RectangleF destRect, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRect( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public void EnumerateMetafile( + Metafile metafile, + Rectangle destRect, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestRectI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + ref destRect, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + PointF[] destPoints, + RectangleF srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (PointF* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPoints( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + public unsafe void EnumerateMetafile( + Metafile metafile, + Point[] destPoints, + Rectangle srcRect, + GraphicsUnit unit, + EnumerateMetafileProc callback, + IntPtr callbackData, + ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(destPoints); + + if (destPoints.Length != 3) + throw new ArgumentException(SR.GdiplusDestPointsInvalidParallelogram); + + fixed (Point* p = destPoints) + { + Gdip.CheckStatus(Gdip.GdipEnumerateMetafileSrcRectDestPointsI( + new HandleRef(this, NativeGraphics), + new HandleRef(metafile, metafile?.nativeImage ?? IntPtr.Zero), + p, destPoints.Length, + ref srcRect, + unit, + callback, + callbackData, + new HandleRef(imageAttr, imageAttr?.nativeImageAttributes ?? IntPtr.Zero))); + } + } + + /// + /// Combines current Graphics context with all previous contexts. + /// When BeginContainer() is called, a copy of the current context is pushed into the GDI+ context stack, it keeps track of the + /// absolute clipping and transform but reset the public properties so it looks like a brand new context. + /// When Save() is called, a copy of the current context is also pushed in the GDI+ stack but the public clipping and transform + /// properties are not reset (cumulative). Consecutive Save context are ignored with the exception of the top one which contains + /// all previous information. + /// The return value is an object array where the first element contains the cumulative clip region and the second the cumulative + /// translate transform matrix. + /// WARNING: This method is for internal FX support only. + /// + [EditorBrowsable(EditorBrowsableState.Never)] +#if NETCOREAPP + [Obsolete("Use the Graphics.GetContextInfo overloads that accept arguments for better performance and fewer allocations.", DiagnosticId = "SYSLIB0016", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] +#endif + [SupportedOSPlatform("windows")] + public object GetContextInfo() + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out Region? cumulativeClip); + return new object[] { cumulativeClip ?? new Region(), new Matrix(cumulativeTransform) }; + } + + private void GetContextInfo(out Matrix3x2 cumulativeTransform, bool calculateClip, out Region? cumulativeClip) + { + cumulativeClip = calculateClip ? GetRegionIfNotInfinite() : null; // Current context clip. + cumulativeTransform = TransformElements; // Current context transform. + Vector2 currentOffset = default; // Offset of current context. + Vector2 totalOffset = default; // Absolute coordinate offset of top context. + + GraphicsContext? context = _previousContext; + + if (!cumulativeTransform.IsIdentity) + { + currentOffset = cumulativeTransform.Translation; + } + + while (context is not null) + { + if (!context.TransformOffset.IsEmpty()) + { + cumulativeTransform.Translate(context.TransformOffset); + } + + if (!currentOffset.IsEmpty()) + { + // The location of the GDI+ clip region is relative to the coordinate origin after any translate transform + // has been applied. We need to intersect regions using the same coordinate origin relative to the previous + // context. + + // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. + cumulativeClip?.Translate(currentOffset.X, currentOffset.Y); + totalOffset.X += currentOffset.X; + totalOffset.Y += currentOffset.Y; + } + + // Context only stores clips if they are not infinite. Intersecting a clip with an infinite clip is a no-op. + if (calculateClip && context.Clip is not null) + { + // Intersecting an infinite clip with another is just a copy of the second clip. + if (cumulativeClip is null) + { + cumulativeClip = context.Clip; + } + else + { + cumulativeClip.Intersect(context.Clip); + } + } + + currentOffset = context.TransformOffset; + + // Ignore subsequent cumulative contexts. + do + { + context = context.Previous; + + if (context == null || !context.Next!.IsCumulative) + { + break; + } + } while (context.IsCumulative); + } + + if (!totalOffset.IsEmpty()) + { + // We need now to reset the total transform in the region so when calling Region.GetHRgn(Graphics) + // the HRegion is properly offset by GDI+ based on the total offset of the graphics object. + + // If we don't have a cumulative clip, we're infinite, and translation on infinite regions is a no-op. + cumulativeClip?.Translate(-totalOffset.X, -totalOffset.Y); + } + } + +#if NETCOREAPP3_1_OR_GREATER + /// + /// Gets the cumulative offset. + /// + /// The cumulative offset. + [EditorBrowsable(EditorBrowsableState.Never)] + [SupportedOSPlatform("windows")] + public void GetContextInfo(out PointF offset) + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: false, out _); + Vector2 translation = cumulativeTransform.Translation; + offset = new PointF(translation.X, translation.Y); + } + + /// + /// Gets the cumulative offset and clip region. + /// + /// The cumulative offset. + /// The cumulative clip region or null if the clip region is infinite. + [EditorBrowsable(EditorBrowsableState.Never)] + [SupportedOSPlatform("windows")] + public void GetContextInfo(out PointF offset, out Region? clip) + { + GetContextInfo(out Matrix3x2 cumulativeTransform, calculateClip: true, out clip); + Vector2 translation = cumulativeTransform.Translation; + offset = new PointF(translation.X, translation.Y); + } +#endif + + public RectangleF VisibleClipBounds + { + get + { + if (PrintingHelper is PrintPreviewGraphics ppGraphics) + return ppGraphics.VisibleClipBounds; + + Gdip.CheckStatus(Gdip.GdipGetVisibleClipBounds(new HandleRef(this, NativeGraphics), out RectangleF rect)); + + return rect; + } + } + + /// + /// Saves the current context into the context stack. + /// + private void PushContext(GraphicsContext context) + { + Debug.Assert(context != null && context.State != 0, "GraphicsContext object is null or not valid."); + + if (_previousContext != null) + { + // Push context. + context.Previous = _previousContext; + _previousContext.Next = context; + } + _previousContext = context; + } + + /// + /// Pops all contexts from the specified one included. The specified context is becoming the current context. + /// + private void PopContext(int currentContextState) + { + Debug.Assert(_previousContext != null, "Trying to restore a context when the stack is empty"); + GraphicsContext? context = _previousContext; + + // Pop all contexts up the stack. + while (context != null) + { + if (context.State == currentContextState) + { + _previousContext = context.Previous; + + // This will dispose all context object up the stack. + context.Dispose(); + return; + } + context = context.Previous; + } + Debug.Fail("Warning: context state not found!"); + } + + public GraphicsState Save() + { + GraphicsContext context = new GraphicsContext(this); + int status = Gdip.GdipSaveGraphics(new HandleRef(this, NativeGraphics), out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + context.IsCumulative = true; + PushContext(context); + + return new GraphicsState(state); + } + + public void Restore(GraphicsState gstate) + { + Gdip.CheckStatus(Gdip.GdipRestoreGraphics(new HandleRef(this, NativeGraphics), gstate.nativeState)); + PopContext(gstate.nativeState); + } + + public GraphicsContainer BeginContainer(RectangleF dstrect, RectangleF srcrect, GraphicsUnit unit) + { + GraphicsContext context = new GraphicsContext(this); + + int status = Gdip.GdipBeginContainer( + new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public GraphicsContainer BeginContainer() + { + GraphicsContext context = new GraphicsContext(this); + int status = Gdip.GdipBeginContainer2(new HandleRef(this, NativeGraphics), out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public void EndContainer(GraphicsContainer container) + { + ArgumentNullException.ThrowIfNull(container); + + Gdip.CheckStatus(Gdip.GdipEndContainer(new HandleRef(this, NativeGraphics), container.nativeGraphicsContainer)); + PopContext(container.nativeGraphicsContainer); + } + + public GraphicsContainer BeginContainer(Rectangle dstrect, Rectangle srcrect, GraphicsUnit unit) + { + GraphicsContext context = new GraphicsContext(this); + + int status = Gdip.GdipBeginContainerI( + new HandleRef(this, NativeGraphics), ref dstrect, ref srcrect, unit, out int state); + + if (status != Gdip.Ok) + { + context.Dispose(); + throw Gdip.StatusException(status); + } + + context.State = state; + PushContext(context); + + return new GraphicsContainer(state); + } + + public void AddMetafileComment(byte[] data) + { + ArgumentNullException.ThrowIfNull(data); + + Gdip.CheckStatus(Gdip.GdipComment(new HandleRef(this, NativeGraphics), data.Length, data)); + } + + public static IntPtr GetHalftonePalette() + { + if (s_halftonePalette == IntPtr.Zero) + { + lock (s_syncObject) + { + if (s_halftonePalette == IntPtr.Zero) + { + AppDomain.CurrentDomain.DomainUnload += OnDomainUnload; + AppDomain.CurrentDomain.ProcessExit += OnDomainUnload; + + s_halftonePalette = Gdip.GdipCreateHalftonePalette(); + } + } + } + return s_halftonePalette; + } + + // This is called from AppDomain.ProcessExit and AppDomain.DomainUnload. + private static void OnDomainUnload(object? sender, EventArgs e) + { + if (s_halftonePalette != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(s_halftonePalette); + s_halftonePalette = IntPtr.Zero; + } + } + + /// + /// GDI+ will return a 'generic error' with specific win32 last error codes when + /// a terminal server session has been closed, minimized, etc... We don't want + /// to throw when this happens, so we'll guard against this by looking at the + /// 'last win32 error code' and checking to see if it is either 1) access denied + /// or 2) proc not found and then ignore it. + /// + /// The problem is that when you lock the machine, the secure desktop is enabled and + /// rendering fails which is expected (since the app doesn't have permission to draw + /// on the secure desktop). Not sure if there's anything you can do, short of catching + /// the desktop switch message and absorbing all the exceptions that get thrown while + /// it's the secure desktop. + /// + private static void CheckErrorStatus(int status) + { + if (status == Gdip.Ok) + return; + + // Generic error from GDI+ can be GenericError or Win32Error. + if (status == Gdip.GenericError || status == Gdip.Win32Error) + { + int error = Marshal.GetLastWin32Error(); + if (error == SafeNativeMethods.ERROR_ACCESS_DENIED || error == SafeNativeMethods.ERROR_PROC_NOT_FOUND || + // Here, we'll check to see if we are in a terminal services session... + (((Interop.User32.GetSystemMetrics(NativeMethods.SM_REMOTESESSION) & 0x00000001) != 0) && (error == 0))) + { + return; + } + } + + // Legitimate error, throw our status exception. + throw Gdip.StatusException(status); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs b/src/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs new file mode 100644 index 00000000000..bc37013170f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/GraphicsContext.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; + +namespace System.Drawing +{ + /// + /// Contains information about the context of a Graphics object. + /// + internal sealed class GraphicsContext : IDisposable + { + public GraphicsContext(Graphics g) + { + TransformOffset = g.TransformElements.Translation; + Clip = g.GetRegionIfNotInfinite(); + } + + /// + /// Disposes this and all contexts up the stack. + /// + public void Dispose() + { + // Dispose all contexts up the stack since they are relative to this one and its state will be invalid. + Next?.Dispose(); + Next = null; + + Clip?.Dispose(); + Clip = null; + + GC.SuppressFinalize(this); + } + + /// + /// The state id representing the GraphicsContext. + /// + public int State { get; set; } + + /// + /// The translate transform in the GraphicsContext. + /// + public Vector2 TransformOffset { get; private set; } + + /// + /// The clipping region the GraphicsContext. + /// + public Region? Clip { get; private set; } + + /// + /// The next GraphicsContext object in the stack. + /// + public GraphicsContext? Next { get; set; } + + /// + /// The previous GraphicsContext object in the stack. + /// + public GraphicsContext? Previous { get; set; } + + /// + /// Flag that determines whether the context was created for a Graphics.Save() operation. + /// This kind of contexts are cumulative across subsequent Save() calls so the top context + /// info is cumulative. This is not the same for contexts created for a Graphics.BeginContainer() + /// operation, in this case the new context information is reset. See Graphics.BeginContainer() + /// and Graphics.Save() for more information. + /// + public bool IsCumulative { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/GraphicsUnit.cs b/src/System.Drawing.Common/src/System/Drawing/GraphicsUnit.cs new file mode 100644 index 00000000000..8c2fc0e7150 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/GraphicsUnit.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the unit of measure for the given data. + /// + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public enum GraphicsUnit + { + /// + /// Specifies the world unit as the unit of measure. + /// + World = 0, + /// + /// Specifies 1/75 inch as the unit of measure. + /// + Display = 1, + /// + /// Specifies a device pixel as the unit of measure. + /// + Pixel = 2, + /// + /// Specifies a printer's point (1/72 inch) as the unit of measure. + /// + Point = 3, + /// + /// Specifies the inch as the unit of measure. + /// + Inch = 4, + /// + /// Specifies the document unit (1/300 inch) as the unit of measure. + /// + Document = 5, + /// + /// Specifies the millimeter as the unit of measure. + /// + Millimeter = 6 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/IDeviceContext.cs b/src/System.Drawing.Common/src/System/Drawing/IDeviceContext.cs new file mode 100644 index 00000000000..349770b8ccf --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/IDeviceContext.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// This interface defines methods for obtaining a display/window device context handle (Win32 hdc). + /// Note: Display and window dc handles are obtained and released using BeginPaint/EndPaint and + /// GetDC/ReleaseDC; this interface is intended to be used with the last method only. + /// + /// Warning to implementors: Creating and releasing non-display dc handles using this interface needs + /// special care, for instance using other Win32 functions like CreateDC or CreateCompatibleDC require + /// DeleteDC instead of ReleaseDC to properly free the dc handle. + /// + /// See the DeviceContext class for an implementation of this interface, it uses the Dispose method + /// for freeing non-display dc handles. + /// + /// This is a low-level API that is expected to be used with TextRenderer or PInvoke calls. + /// + public interface IDeviceContext : IDisposable + { + IntPtr GetHdc(); + + void ReleaseHdc(); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Icon.ComWrappers.cs b/src/System.Drawing.Common/src/System/Drawing/Icon.ComWrappers.cs new file mode 100644 index 00000000000..cdb3698f987 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Icon.ComWrappers.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { + public unsafe void Save(Stream outputStream) + { + if (_iconData != null) + { + outputStream.Write(_iconData, 0, _iconData.Length); + } + else + { + if (outputStream == null) + throw new ArgumentNullException(nameof(outputStream)); + + // Ideally, we would pick apart the icon using + // GetIconInfo, and then pull the individual bitmaps out, + // converting them to DIBS and saving them into the file. + // But, in the interest of simplicity, we just call to + // OLE to do it for us. + PICTDESC pictdesc = PICTDESC.CreateIconPICTDESC(Handle); + Guid iid = DrawingCom.IPicture.IID; + IntPtr lpPicture; + Marshal.ThrowExceptionForHR(OleCreatePictureIndirect(&pictdesc, &iid, fOwn: 0, &lpPicture)); + + IntPtr streamPtr = IntPtr.Zero; + try + { + // Use UniqueInstance here because we never want to cache the wrapper. It only gets used once and then disposed. + using DrawingCom.IPicture picture = (DrawingCom.IPicture)DrawingCom.Instance + .GetOrCreateObjectForComInstance(lpPicture, CreateObjectFlags.UniqueInstance); + + var gpStream = new GPStream(outputStream, makeSeekable: false); + streamPtr = DrawingCom.Instance.GetOrCreateComInterfaceForObject(gpStream, CreateComInterfaceFlags.None); + + DrawingCom.ThrowExceptionForHR(picture.SaveAsFile(streamPtr, -1, null)); + } + finally + { + if (streamPtr != IntPtr.Zero) + { + int count = Marshal.Release(streamPtr); + Debug.Assert(count == 0); + } + + if (lpPicture != IntPtr.Zero) + { + int count = Marshal.Release(lpPicture); + Debug.Assert(count == 0); + } + } + } + } + +#if NET7_0_OR_GREATER + [LibraryImport(Interop.Libraries.Oleaut32)] + private static unsafe partial int OleCreatePictureIndirect( +#else + [DllImport(Interop.Libraries.Oleaut32)] + private static extern unsafe int OleCreatePictureIndirect( +#endif + PICTDESC* pictdesc, + Guid* refiid, + int fOwn, + IntPtr* lplpvObj); + + [StructLayout(LayoutKind.Sequential)] + private readonly struct PICTDESC + { + public readonly int SizeOfStruct; + public readonly int PicType; + public readonly IntPtr Icon; + + private unsafe PICTDESC(int picType, IntPtr hicon) + { + SizeOfStruct = sizeof(PICTDESC); + PicType = picType; + Icon = hicon; + } + + public static PICTDESC CreateIconPICTDESC(IntPtr hicon) => + new PICTDESC(Ole.PICTYPE_ICON, hicon); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Icon.cs b/src/System.Drawing.Common/src/System/Drawing/Icon.cs new file mode 100644 index 00000000000..a3d7c5cce06 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Icon.cs @@ -0,0 +1,882 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace System.Drawing +{ + [Editor("System.Drawing.Design.IconEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [TypeConverter(typeof(IconConverter))] + [Serializable] + [TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed partial class Icon : MarshalByRefObject, ICloneable, IDisposable, ISerializable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + private static int s_bitDepth; + + // The PNG signature is specified at http://www.w3.org/TR/PNG/#5PNG-file-signature + private const int PNGSignature1 = 137 + ('P' << 8) + ('N' << 16) + ('G' << 24); + private const int PNGSignature2 = 13 + (10 << 8) + (26 << 16) + (10 << 24); + + // Icon data + private readonly byte[]? _iconData; + private uint _bestImageOffset; + private uint _bestBitDepth; + private uint _bestBytesInRes; + private bool? _isBestImagePng; + private Size _iconSize = Size.Empty; + private IntPtr _handle = IntPtr.Zero; + private readonly bool _ownHandle = true; + + private Icon() { } + + internal Icon(IntPtr handle) : this(handle, false) + { + } + + internal Icon(IntPtr handle, bool takeOwnership) + { + if (handle == IntPtr.Zero) + { + throw new ArgumentException(SR.Format(SR.InvalidGDIHandle, nameof(Icon))); + } + + _handle = handle; + _ownHandle = takeOwnership; + } + + public Icon(string fileName) : this(fileName, 0, 0) + { + } + + public Icon(string fileName, Size size) : this(fileName, size.Width, size.Height) + { + } + + public Icon(string fileName, int width, int height) : this() + { + using (FileStream f = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + Debug.Assert(f != null, "File.OpenRead returned null instead of throwing an exception"); + _iconData = new byte[(int)f.Length]; + f.Read(_iconData, 0, _iconData.Length); + } + + Initialize(width, height); + } + + public Icon(Icon original, Size size) : this(original, size.Width, size.Height) + { + } + + public Icon(Icon original, int width, int height) : this() + { + ArgumentNullException.ThrowIfNull(original); + + _iconData = original._iconData; + + if (_iconData == null) + { + _iconSize = original.Size; + _handle = Interop.User32.CopyImage(new HandleRef(original, original.Handle), SafeNativeMethods.IMAGE_ICON, _iconSize.Width, _iconSize.Height, 0); + } + else + { + Initialize(width, height); + } + } + + public Icon(Type type, string resource) : this() + { + ArgumentNullException.ThrowIfNull(resource); + + Stream? stream = type.Module.Assembly.GetManifestResourceStream(type, resource); + if (stream == null) + { + throw new ArgumentException(SR.Format(SR.ResourceNotFound, type, resource)); + } + + _iconData = new byte[(int)stream.Length]; + stream.Read(_iconData, 0, _iconData.Length); + Initialize(0, 0); + } + + public Icon(Stream stream) : this(stream, 0, 0) + { + } + + public Icon(Stream stream, Size size) : this(stream, size.Width, size.Height) + { + } + + public Icon(Stream stream, int width, int height) : this() + { + ArgumentNullException.ThrowIfNull(stream); + + _iconData = new byte[(int)stream.Length]; +#if NET7_0_OR_GREATER + stream.ReadExactly(_iconData); +#else + int totalRead = 0; + while (totalRead < _iconData.Length) + { + int bytesRead = stream.Read(_iconData, totalRead, _iconData.Length - totalRead); + if (bytesRead <= 0) + { + throw new EndOfStreamException(); + } + totalRead += bytesRead; + } +#endif + Initialize(width, height); + } + + private Icon(SerializationInfo info, StreamingContext context) + { + _iconData = (byte[])info.GetValue("IconData", typeof(byte[]))!; // Do not rename (binary serialization) + _iconSize = (Size)info.GetValue("IconSize", typeof(Size))!; // Do not rename (binary serialization) + Initialize(_iconSize.Width, _iconSize.Height); + } + + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) + { + if (_iconData != null) + { + si.AddValue("IconData", _iconData, typeof(byte[])); // Do not rename (binary serialization) + } + else + { + MemoryStream stream = new MemoryStream(); + Save(stream); + si.AddValue("IconData", stream.ToArray(), typeof(byte[])); // Do not rename (binary serialization) + } + + si.AddValue("IconSize", _iconSize, typeof(Size)); // Do not rename (binary serialization) + } + + public static Icon? ExtractAssociatedIcon(string filePath) => ExtractAssociatedIcon(filePath, 0); + + private static unsafe Icon? ExtractAssociatedIcon(string filePath, int index) + { + ArgumentNullException.ThrowIfNull(filePath); + if (string.IsNullOrEmpty(filePath)) + throw new ArgumentException(SR.NullOrEmptyPath, nameof(filePath)); + + filePath = Path.GetFullPath(filePath); + if (!File.Exists(filePath)) + { + throw new FileNotFoundException(message: null, fileName: filePath); + } + + // ExtractAssociatedIcon copies the loaded path into the buffer that it is passed. + // It isn't clear what the exact semantics are for copying back the path, a quick + // look at the code it might be hard coded to 128 chars for some cases. Leaving the + // historical MAX_PATH as a minimum to be safe. + + char[] buffer = ArrayPool.Shared.Rent(Math.Max(NativeMethods.MAX_PATH, filePath.Length)); + filePath.CopyTo(0, buffer, 0, filePath.Length); + buffer[filePath.Length] = '\0'; + + IntPtr hIcon; + fixed (char* b = buffer) + { + hIcon = Interop.Shell32.ExtractAssociatedIcon(NativeMethods.NullHandleRef, b, ref index); + } + + ArrayPool.Shared.Return(buffer); + + if (hIcon != IntPtr.Zero) + { + return new Icon(hIcon, true); + } + + return null; + } + + [Browsable(false)] + public IntPtr Handle + { + get + { + if (_handle == IntPtr.Zero) + { + throw new ObjectDisposedException(GetType().Name); + } + return _handle; + } + } + + [Browsable(false)] + public int Height => Size.Height; + + public unsafe Size Size + { + get + { + if (_iconSize.IsEmpty) + { + Interop.User32.ICONINFO info = default; + Interop.User32.GetIconInfo(new HandleRef(this, Handle), ref info); + Interop.Gdi32.BITMAP bitmap = default; + + if (info.hbmColor != IntPtr.Zero) + { + Interop.Gdi32.GetObject( + new HandleRef(null, info.hbmColor), + sizeof(Interop.Gdi32.BITMAP), + ref bitmap); + Interop.Gdi32.DeleteObject(info.hbmColor); + _iconSize = new Size((int)bitmap.bmWidth, (int)bitmap.bmHeight); + } + else if (info.hbmMask != IntPtr.Zero) + { + Interop.Gdi32.GetObject( + new HandleRef(null, info.hbmMask), + sizeof(Interop.Gdi32.BITMAP), + ref bitmap); + _iconSize = new Size((int)bitmap.bmWidth, (int)(bitmap.bmHeight / 2)); + } + + if (info.hbmMask != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(info.hbmMask); + } + } + + return _iconSize; + } + } + + [Browsable(false)] + public int Width => Size.Width; + + public object Clone() => new Icon(this, Size.Width, Size.Height); + + // Called when this object is going to destroy it's Win32 handle. You + // may override this if there is something special you need to do to + // destroy the handle. This will be called even if the handle is not + // owned by this object, which is handy if you want to create a + // derived class that has it's own create/destroy semantics. + // + // The default implementation will call the appropriate Win32 + // call to destroy the handle if this object currently owns the + // handle. It will do nothing if the object does not currently + // own the handle. + internal void DestroyHandle() + { + if (_ownHandle) + { + Interop.User32.DestroyIcon(new HandleRef(this, _handle)); + _handle = IntPtr.Zero; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (_handle != IntPtr.Zero) + { +#if FINALIZATION_WATCH + if (!disposing) + { + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); + } +#endif + DestroyHandle(); + } + } + + // Draws this image to a graphics object. The drawing command originates on the graphics + // object, but a graphics object generally has no idea how to render a given image. So, + // it passes the call to the actual image. This version crops the image to the given + // dimensions and allows the user to specify a rectangle within the image to draw. + private void DrawIcon(IntPtr dc, Rectangle imageRect, Rectangle targetRect, bool stretch) + { + int imageX = 0; + int imageY = 0; + int imageWidth; + int imageHeight; + int targetX = 0; + int targetY = 0; + int targetWidth; + int targetHeight; + + Size cursorSize = Size; + + // Compute the dimensions of the icon if needed. + if (!imageRect.IsEmpty) + { + imageX = imageRect.X; + imageY = imageRect.Y; + imageWidth = imageRect.Width; + imageHeight = imageRect.Height; + } + else + { + imageWidth = cursorSize.Width; + imageHeight = cursorSize.Height; + } + + if (!targetRect.IsEmpty) + { + targetX = targetRect.X; + targetY = targetRect.Y; + targetWidth = targetRect.Width; + targetHeight = targetRect.Height; + } + else + { + targetWidth = cursorSize.Width; + targetHeight = cursorSize.Height; + } + + int drawWidth, drawHeight; + int clipWidth, clipHeight; + + if (stretch) + { + drawWidth = cursorSize.Width * targetWidth / imageWidth; + drawHeight = cursorSize.Height * targetHeight / imageHeight; + clipWidth = targetWidth; + clipHeight = targetHeight; + } + else + { + drawWidth = cursorSize.Width; + drawHeight = cursorSize.Height; + clipWidth = targetWidth < imageWidth ? targetWidth : imageWidth; + clipHeight = targetHeight < imageHeight ? targetHeight : imageHeight; + } + + // The ROP is SRCCOPY, so we can be simple here and take + // advantage of clipping regions. Drawing the cursor + // is merely a matter of offsetting and clipping. + IntPtr hSaveRgn = SaveClipRgn(dc); + try + { + Interop.Gdi32.IntersectClipRect(new HandleRef(this, dc), targetX, targetY, targetX + clipWidth, targetY + clipHeight); + Interop.User32.DrawIconEx(new HandleRef(null, dc), + targetX - imageX, + targetY - imageY, + new HandleRef(this, _handle), + drawWidth, + drawHeight, + 0, + NativeMethods.NullHandleRef, + SafeNativeMethods.DI_NORMAL); + } + finally + { + Interop.Gdi32.SelectClipRgn(dc, hSaveRgn); + // We need to delete the region handle after restoring the region as GDI+ uses a copy of the handle. + Interop.Gdi32.DeleteObject(hSaveRgn); + } + } + + private static IntPtr SaveClipRgn(IntPtr hDC) + { + IntPtr hTempRgn = Interop.Gdi32.CreateRectRgn(0, 0, 0, 0); + IntPtr hSaveRgn = IntPtr.Zero; + + int result = Interop.Gdi32.GetClipRgn(hDC, hTempRgn); + if (result > 0) + { + hSaveRgn = hTempRgn; + } + else + { + // if we fail to get the clip region delete the handle. + Interop.Gdi32.DeleteObject(hTempRgn); + } + + return hSaveRgn; + } + + internal void Draw(Graphics graphics, int x, int y) + { + Size size = Size; + Draw(graphics, new Rectangle(x, y, size.Width, size.Height)); + } + + // Draws this image to a graphics object. The drawing command originates on the graphics + // object, but a graphics object generally has no idea how to render a given image. So, + // it passes the call to the actual image. This version stretches the image to the given + // dimensions and allows the user to specify a rectangle within the image to draw. + internal void Draw(Graphics graphics, Rectangle targetRect) + { + Rectangle copy = targetRect; + + using Matrix transform = graphics.Transform; + PointF offset = transform.Offset; + copy.X += (int)offset.X; + copy.Y += (int)offset.Y; + + using (WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping)) + { + IntPtr dc = wg.GetHdc(); + DrawIcon(dc, Rectangle.Empty, copy, true); + } + } + + // Draws this image to a graphics object. The drawing command originates on the graphics + // object, but a graphics object generally has no idea how to render a given image. So, + // it passes the call to the actual image. This version crops the image to the given + // dimensions and allows the user to specify a rectangle within the image to draw. + internal void DrawUnstretched(Graphics graphics, Rectangle targetRect) + { + Rectangle copy = targetRect; + using Matrix transform = graphics.Transform; + PointF offset = transform.Offset; + copy.X += (int)offset.X; + copy.Y += (int)offset.Y; + + using (WindowsGraphics wg = WindowsGraphics.FromGraphics(graphics, ApplyGraphicsProperties.Clipping)) + { + IntPtr dc = wg.GetHdc(); + DrawIcon(dc, Rectangle.Empty, copy, false); + } + } + + ~Icon() => Dispose(false); + + public static Icon FromHandle(IntPtr handle) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(null, nameof(handle)); + + return new Icon(handle); + } + + // Initializes this Image object. This is identical to calling the image's + // constructor with picture, but this allows non-constructor initialization, + // which may be necessary in some instances. + private unsafe void Initialize(int width, int height) + { + if (_iconData == null || _handle != IntPtr.Zero) + { + throw new InvalidOperationException(SR.Format(SR.IllegalState, GetType().Name)); + } + + if (_iconData.Length < sizeof(SafeNativeMethods.ICONDIR)) + { + throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon))); + } + + // Get the correct width and height. + if (width == 0) + { + width = Interop.User32.GetSystemMetrics(SafeNativeMethods.SM_CXICON); + } + + if (height == 0) + { + height = Interop.User32.GetSystemMetrics(SafeNativeMethods.SM_CYICON); + } + + if (s_bitDepth == 0) + { + IntPtr dc = Interop.User32.GetDC(IntPtr.Zero); + s_bitDepth = Interop.Gdi32.GetDeviceCaps(dc, Interop.Gdi32.DeviceCapability.BITSPIXEL); + s_bitDepth *= Interop.Gdi32.GetDeviceCaps(dc, Interop.Gdi32.DeviceCapability.PLANES); + Interop.User32.ReleaseDC(IntPtr.Zero, dc); + + // If the bitdepth is 8, make it 4 because windows does not + // choose a 256 color icon if the display is running in 256 color mode + // due to palette flicker. + if (s_bitDepth == 8) + { + s_bitDepth = 4; + } + } + + fixed (byte* b = _iconData) + { + SafeNativeMethods.ICONDIR* dir = (SafeNativeMethods.ICONDIR*)b; + + if (dir->idReserved != 0 || dir->idType != 1 || dir->idCount == 0) + { + throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon))); + } + + byte bestWidth = 0; + byte bestHeight = 0; + + if (sizeof(SafeNativeMethods.ICONDIRENTRY) * (dir->idCount - 1) + sizeof(SafeNativeMethods.ICONDIR) + > _iconData.Length) + { + throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon))); + } + + var entries = new ReadOnlySpan(&dir->idEntries, dir->idCount); + foreach (SafeNativeMethods.ICONDIRENTRY entry in entries) + { + bool fUpdateBestFit = false; + uint iconBitDepth; + if (entry.bColorCount != 0) + { + iconBitDepth = 4; + if (entry.bColorCount < 0x10) + { + iconBitDepth = 1; + } + } + else + { + iconBitDepth = entry.wBitCount; + } + + // If it looks like if nothing is specified at this point then set the bits per pixel to 8. + if (iconBitDepth == 0) + { + iconBitDepth = 8; + } + + // Windows rules for specifing an icon: + // + // 1. The icon with the closest size match. + // 2. For matching sizes, the image with the closest bit depth. + // 3. If there is no color depth match, the icon with the closest color depth that does not exceed the display. + // 4. If all icon color depth > display, lowest color depth is chosen. + // 5. color depth of > 8bpp are all equal. + // 6. Never choose an 8bpp icon on an 8bpp system. + + if (_bestBytesInRes == 0) + { + fUpdateBestFit = true; + } + else + { + int bestDelta = Math.Abs(bestWidth - width) + Math.Abs(bestHeight - height); + int thisDelta = Math.Abs(entry.bWidth - width) + Math.Abs(entry.bHeight - height); + + if ((thisDelta < bestDelta) || + (thisDelta == bestDelta && (iconBitDepth <= s_bitDepth && iconBitDepth > _bestBitDepth || _bestBitDepth > s_bitDepth && iconBitDepth < _bestBitDepth))) + { + fUpdateBestFit = true; + } + } + + if (fUpdateBestFit) + { + bestWidth = entry.bWidth; + bestHeight = entry.bHeight; + _bestImageOffset = entry.dwImageOffset; + _bestBytesInRes = entry.dwBytesInRes; + _bestBitDepth = iconBitDepth; + } + } + + if (_bestImageOffset > int.MaxValue) + { + throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon))); + } + + if (_bestBytesInRes > int.MaxValue) + { + throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER); + } + + uint endOffset; + try + { + endOffset = checked(_bestImageOffset + _bestBytesInRes); + } + catch (OverflowException) + { + throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER); + } + + if (endOffset > _iconData.Length) + { + throw new ArgumentException(SR.Format(SR.InvalidPictureType, "picture", nameof(Icon))); + } + + // Copy the bytes into an aligned buffer if needed. + if ((_bestImageOffset % IntPtr.Size) != 0) + { + // Beginning of icon's content is misaligned. + byte[] alignedBuffer = ArrayPool.Shared.Rent((int)_bestBytesInRes); + Array.Copy(_iconData, _bestImageOffset, alignedBuffer, 0, _bestBytesInRes); + + fixed (byte* pbAlignedBuffer = alignedBuffer) + { + _handle = Interop.User32.CreateIconFromResourceEx(pbAlignedBuffer, _bestBytesInRes, true, 0x00030000, 0, 0, 0); + } + ArrayPool.Shared.Return(alignedBuffer); + } + else + { + try + { + _handle = Interop.User32.CreateIconFromResourceEx(checked(b + _bestImageOffset), _bestBytesInRes, true, 0x00030000, 0, 0, 0); + } + catch (OverflowException) + { + throw new Win32Exception(SafeNativeMethods.ERROR_INVALID_PARAMETER); + } + } + + if (_handle == IntPtr.Zero) + { + throw new Win32Exception(); + } + } + } + + private unsafe void CopyBitmapData(BitmapData sourceData, BitmapData targetData) + { + byte* srcPtr = (byte*)sourceData.Scan0; + byte* destPtr = (byte*)targetData.Scan0; + + Debug.Assert(sourceData.Height == targetData.Height, "Unexpected height. How did this happen?"); + int height = Math.Min(sourceData.Height, targetData.Height); + long bytesToCopyEachIter = Math.Abs(targetData.Stride); + + for (int i = 0; i < height; i++) + { + Buffer.MemoryCopy(srcPtr, destPtr, bytesToCopyEachIter, bytesToCopyEachIter); + srcPtr += sourceData.Stride; + destPtr += targetData.Stride; + } + + GC.KeepAlive(this); // finalizer mustn't deallocate data blobs while this method is running + } + + private static bool BitmapHasAlpha(BitmapData bmpData) + { + bool hasAlpha = false; + for (int i = 0; i < bmpData.Height; i++) + { + for (int j = 3; j < Math.Abs(bmpData.Stride); j += 4) + { + // Stride here is fine since we know we're doing this on the whole image. + unsafe + { + byte* candidate = unchecked(((byte*)bmpData.Scan0.ToPointer()) + (i * bmpData.Stride) + j); + if (*candidate != 0) + { + hasAlpha = true; + return hasAlpha; + } + } + } + } + + return false; + } + + public Bitmap ToBitmap() + { + // DontSupportPngFramesInIcons is true when the application is targeting framework version below 4.6 + // and false when the application is targeting 4.6 and above. Downlevel application can also set the following switch + // to false in the .config file's runtime section in order to opt-in into the new behavior: + // + if (HasPngSignature() && !LocalAppContextSwitches.DontSupportPngFramesInIcons) + { + return PngFrame(); + } + + return BmpFrame(); + } + + private unsafe Bitmap BmpFrame() + { + Bitmap? bitmap = null; + if (_iconData != null && _bestBitDepth == 32) + { + // GDI+ doesnt handle 32 bpp icons with alpha properly + // we load the icon ourself from the byte table + bitmap = new Bitmap(Size.Width, Size.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + Debug.Assert(_bestImageOffset >= 0 && (_bestImageOffset + _bestBytesInRes) <= _iconData.Length, "Illegal offset/length for the Icon data"); + + unsafe + { + BitmapData bmpdata = bitmap.LockBits(new Rectangle(0, 0, Size.Width, Size.Height), + ImageLockMode.WriteOnly, + PixelFormat.Format32bppArgb); + try + { + uint* pixelPtr = (uint*)bmpdata.Scan0.ToPointer(); + + // jumping the image header + int newOffset = (int)(_bestImageOffset + sizeof(NativeMethods.BITMAPINFOHEADER)); + // there is no color table that we need to skip since we're 32bpp + + int lineLength = Size.Width * 4; + int width = Size.Width; + for (int j = (Size.Height - 1) * 4; j >= 0; j -= 4) + { + Marshal.Copy(_iconData, newOffset + j * width, (IntPtr)pixelPtr, lineLength); + pixelPtr += width; + } + + // note: we ignore the mask that's available after the pixel table + } + finally + { + bitmap.UnlockBits(bmpdata); + } + } + } + else if (_bestBitDepth == 0 || _bestBitDepth == 32) + { + // This may be a 32bpp icon or an icon without any data. + Interop.User32.ICONINFO info = default; + Interop.User32.GetIconInfo(new HandleRef(this, _handle), ref info); + Interop.Gdi32.BITMAP bmp = default; + try + { + if (info.hbmColor != IntPtr.Zero) + { + Interop.Gdi32.GetObject(new HandleRef(null, info.hbmColor), sizeof(Interop.Gdi32.BITMAP), ref bmp); + if (bmp.bmBitsPixel == 32) + { + Bitmap? tmpBitmap = null; + BitmapData? bmpData = null; + BitmapData? targetData = null; + try + { + tmpBitmap = Image.FromHbitmap(info.hbmColor); + + // In GDI+ the bits are there but the bitmap was created with no alpha channel + // so copy the bits by hand to a new bitmap + // we also need to go around a limitation in the way the ICON is stored (ie if it's another bpp + // but stored in 32bpp all pixels are transparent and not opaque) + // (Here you mostly need to remain calm....) + bmpData = tmpBitmap.LockBits(new Rectangle(0, 0, tmpBitmap.Width, tmpBitmap.Height), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat); + + // we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data + if (BitmapHasAlpha(bmpData)) + { + bitmap = new Bitmap(bmpData.Width, bmpData.Height, PixelFormat.Format32bppArgb); + targetData = bitmap.LockBits(new Rectangle(0, 0, bmpData.Width, bmpData.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); + + CopyBitmapData(bmpData, targetData); + } + } + finally + { + if (tmpBitmap != null && bmpData != null) + { + tmpBitmap.UnlockBits(bmpData); + } + if (bitmap != null && targetData != null) + { + bitmap.UnlockBits(targetData); + } + } + tmpBitmap.Dispose(); + } + } + } + finally + { + if (info.hbmColor != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(info.hbmColor); + } + if (info.hbmMask != IntPtr.Zero) + { + Interop.Gdi32.DeleteObject(info.hbmMask); + } + } + } + + + if (bitmap == null) + { + // last chance... all the other cases (ie non 32 bpp icons coming from a handle or from the bitmapData) + + // we have to do this rather than just return Bitmap.FromHIcon because + // the bitmap returned from that, even though it's 32bpp, just paints where the mask allows it + // seems like another GDI+ weirdness. might be interesting to investigate further. In the meantime + // this looks like the right thing to do and is not more expansive that what was present before. + + Size size = Size; + bitmap = new Bitmap(size.Width, size.Height); // initialized to transparent + Graphics? graphics; + using (graphics = Graphics.FromImage(bitmap)) + { + try + { + using (Bitmap tmpBitmap = Bitmap.FromHicon(Handle)) + { + graphics.DrawImage(tmpBitmap, new Rectangle(0, 0, size.Width, size.Height)); + } + } + catch (ArgumentException) + { + // Sometimes FromHicon will crash with no real reason. + // The backup plan is to just draw the image like we used to. + // NOTE: FromHIcon is also where we have the buffer overrun + // if width and height are mismatched. + Draw(graphics, new Rectangle(0, 0, size.Width, size.Height)); + } + } + + + // GDI+ fills the surface with a sentinel color for GetDC, but does + // not correctly clean it up again, so we have to do it. + Color fakeTransparencyColor = Color.FromArgb(0x0d, 0x0b, 0x0c); + bitmap.MakeTransparent(fakeTransparencyColor); + } + + Debug.Assert(bitmap != null, "Bitmap cannot be null"); + return bitmap; + } + + private Bitmap PngFrame() + { + Debug.Assert(_iconData != null); + using (var stream = new MemoryStream()) + { + stream.Write(_iconData, (int)_bestImageOffset, (int)_bestBytesInRes); + return new Bitmap(stream); + } + } + + private bool HasPngSignature() + { + if (!_isBestImagePng.HasValue) + { + if (_iconData != null && _iconData.Length >= _bestImageOffset + 8) + { + int iconSignature1 = BitConverter.ToInt32(_iconData, (int)_bestImageOffset); + int iconSignature2 = BitConverter.ToInt32(_iconData, (int)_bestImageOffset + 4); + _isBestImagePng = (iconSignature1 == PNGSignature1) && (iconSignature2 == PNGSignature2); + } + else + { + _isBestImagePng = false; + } + } + + return _isBestImagePng.Value; + } + + public override string ToString() => SR.toStringIcon; + + internal static class Ole + { + public const int PICTYPE_ICON = 3; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/IconConverter.cs b/src/System.Drawing.Common/src/System/Drawing/IconConverter.cs new file mode 100644 index 00000000000..f60174f9e46 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/IconConverter.cs @@ -0,0 +1,64 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; + +namespace System.Drawing +{ + public class IconConverter : ExpandableObjectConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + return (sourceType == typeof(byte[])); + } + + public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) + { + return destinationType == typeof(byte[]) || destinationType == typeof(string) + || destinationType == typeof(Image) || destinationType == typeof(Bitmap); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + return value is byte[] bytes ? new Icon(new MemoryStream(bytes)) : base.ConvertFrom(context, culture, value); + } + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (destinationType == typeof(string)) + { + if (value == null) + { + return SR.none; + } + else if (value is Icon) + { + return value.ToString()!; + } + } + else if (destinationType == typeof(byte[])) + { + if (value is Icon icon) + { + using (MemoryStream ms = new MemoryStream()) + { + icon.Save(ms); + return ms.ToArray(); + } + } + } + else if (destinationType == typeof(Image) || destinationType == typeof(Bitmap)) + { + if (value is Icon icon) + { + return icon.ToBitmap(); + } + } + + throw GetConvertFromException(value); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Image.cs b/src/System.Drawing.Common/src/System/Drawing/Image.cs new file mode 100644 index 00000000000..078eb6d5352 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Image.cs @@ -0,0 +1,1096 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Diagnostics.CodeAnalysis; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// An abstract base class that provides functionality for 'Bitmap', 'Icon', 'Cursor', and 'Metafile' descended classes. + /// + [Editor("System.Drawing.Design.ImageEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [ImmutableObject(true)] + [Serializable] + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [TypeConverter(typeof(ImageConverter))] + public abstract class Image : MarshalByRefObject, IDisposable, ICloneable, ISerializable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + // The signature of this delegate is incorrect. The signature of the corresponding + // native callback function is: + // extern "C" { + // typedef BOOL (CALLBACK * ImageAbort)(VOID *); + // typedef ImageAbort DrawImageAbort; + // typedef ImageAbort GetThumbnailImageAbort; + // } + // However, as this delegate is not used in both GDI 1.0 and 1.1, we choose not + // to modify it, in order to preserve compatibility. + public delegate bool GetThumbnailImageAbort(); + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(GetThumbnailImageAbort), MarshalMode.ManagedToUnmanagedIn, typeof(KeepAliveMarshaller))] + internal static class GetThumbnailImageAbortMarshaller + { + internal unsafe struct KeepAliveMarshaller + { + private delegate Interop.BOOL GetThumbnailImageAbortNative(IntPtr callbackdata); + private GetThumbnailImageAbortNative? _managed; + private delegate* unmanaged _nativeFunction; + public void FromManaged(GetThumbnailImageAbort managed) + { + if (managed is null) + { + _managed = null; + _nativeFunction = null; + } + else + { + _managed = data => managed() ? Interop.BOOL.TRUE : Interop.BOOL.FALSE; + _nativeFunction = (delegate* unmanaged)Marshal.GetFunctionPointerForDelegate(_managed); + } + } + + public delegate* unmanaged ToUnmanaged() + { + return _nativeFunction; + } + + public void OnInvoked() + { + GC.KeepAlive(_managed); + } + + public void Free() { } + } + } +#endif + + internal IntPtr nativeImage; + + private object? _userData; + + // used to work around lack of animated gif encoder... rarely set... + private byte[]? _rawData; + + [Localizable(false)] + [DefaultValue(null)] + public object? Tag + { + get => _userData; + set => _userData = value; + } + + private protected Image() { } + + private protected Image(SerializationInfo info, StreamingContext context) + { + byte[] dat = (byte[])info.GetValue("Data", typeof(byte[]))!; // Do not rename (binary serialization) + + try + { + SetNativeImage(InitializeFromStream(new MemoryStream(dat))); + } + catch (ExternalException) + { + } + catch (ArgumentException) + { + } + catch (OutOfMemoryException) + { + } + catch (InvalidOperationException) + { + } + catch (NotImplementedException) + { + } + catch (FileNotFoundException) + { + } + } + + void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) + { + using (MemoryStream stream = new MemoryStream()) + { + Save(stream); + si.AddValue("Data", stream.ToArray(), typeof(byte[])); // Do not rename (binary serialization) + } + } + + /// + /// Creates an from the specified file. + /// + public static Image FromFile(string filename) => FromFile(filename, false); + + public static Image FromFile(string filename, bool useEmbeddedColorManagement) + { + if (!File.Exists(filename)) + { + // Throw a more specific exception for invalid paths that are null or empty, + // contain invalid characters or are too long. + filename = Path.GetFullPath(filename); + throw new FileNotFoundException(filename); + } + + // GDI+ will read this file multiple times. Get the fully qualified path + // so if our app changes default directory we won't get an error + filename = Path.GetFullPath(filename); + + IntPtr image; + + if (useEmbeddedColorManagement) + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromFileICM(filename, out image)); + } + else + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromFile(filename, out image)); + } + + ValidateImage(image); + + Image img = CreateImageObject(image); + EnsureSave(img, filename, null); + return img; + } + + /// + /// Creates an from the specified data stream. + /// + public static Image FromStream(Stream stream) => Image.FromStream(stream, false); + + public static Image FromStream(Stream stream, bool useEmbeddedColorManagement) => FromStream(stream, useEmbeddedColorManagement, true); + + public static Image FromStream(Stream stream, bool useEmbeddedColorManagement, bool validateImageData) + { + ArgumentNullException.ThrowIfNull(stream); + + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement); + + if (validateImageData) + ValidateImage(image); + + Image img = CreateImageObject(image); + EnsureSave(img, null, stream); + return img; + } + + // Used for serialization + private IntPtr InitializeFromStream(Stream stream) + { + IntPtr image = LoadGdipImageFromStream(new GPStream(stream), useEmbeddedColorManagement: false); + ValidateImage(image); + + nativeImage = image; + + Gdip.CheckStatus(Gdip.GdipGetImageType(new HandleRef(this, nativeImage), out _)); + EnsureSave(this, null, stream); + return image; + } + + private static unsafe IntPtr LoadGdipImageFromStream(GPStream stream, bool useEmbeddedColorManagement) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(stream); + + IntPtr image = IntPtr.Zero; + if (useEmbeddedColorManagement) + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStreamICM(streamWrapper.Ptr, &image)); + } + else + { + Gdip.CheckStatus(Gdip.GdipLoadImageFromStream(streamWrapper.Ptr, &image)); + } + return image; + } + + internal Image(IntPtr nativeImage) => SetNativeImage(nativeImage); + + /// + /// Cleans up Windows resources for this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Cleans up Windows resources for this . + /// + ~Image() => Dispose(false); + + /// + /// Creates an exact copy of this . + /// + public object Clone() + { + IntPtr cloneImage; + + Gdip.CheckStatus(Gdip.GdipCloneImage(new HandleRef(this, nativeImage), out cloneImage)); + ValidateImage(cloneImage); + + return CreateImageObject(cloneImage); + } + + protected virtual void Dispose(bool disposing) + { +#if FINALIZATION_WATCH + if (!disposing && nativeImage != IntPtr.Zero) + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); +#endif + if (nativeImage == IntPtr.Zero) + return; + + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDisposeImage(new HandleRef(this, nativeImage)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during Dispose: " + ex.ToString()); + } + finally + { + nativeImage = IntPtr.Zero; + } + } + + /// + /// Saves this to the specified file. + /// + public void Save(string filename) => Save(filename, RawFormat); + + /// + /// Saves this to the specified file in the specified format. + /// + public void Save(string filename, ImageFormat format) + { + ArgumentNullException.ThrowIfNull(format); + + ImageCodecInfo codec = format.FindEncoder() ?? ImageFormat.Png.FindEncoder()!; + + Save(filename, codec, null); + } + + /// + /// Saves this to the specified file in the specified format and with the specified encoder parameters. + /// + public void Save(string filename, ImageCodecInfo encoder, EncoderParameters? encoderParams) + { + ArgumentNullException.ThrowIfNull(filename); + ArgumentNullException.ThrowIfNull(encoder); + ThrowIfDirectoryDoesntExist(filename); + + IntPtr encoderParamsMemory = IntPtr.Zero; + + if (encoderParams != null) + { + _rawData = null; + encoderParamsMemory = encoderParams.ConvertToMemory(); + } + + try + { + Guid g = encoder.Clsid; + bool saved = false; + + if (_rawData != null) + { + ImageCodecInfo? rawEncoder = RawFormat.FindEncoder(); + if (rawEncoder != null && rawEncoder.Clsid == g) + { + using (FileStream fs = File.OpenWrite(filename)) + { + fs.Write(_rawData, 0, _rawData.Length); + saved = true; + } + } + } + + if (!saved) + { + Gdip.CheckStatus(Gdip.GdipSaveImageToFile( + new HandleRef(this, nativeImage), + filename, + ref g, + new HandleRef(encoderParams, encoderParamsMemory))); + } + } + finally + { + if (encoderParamsMemory != IntPtr.Zero) + { + Marshal.FreeHGlobal(encoderParamsMemory); + } + } + } + + private void Save(MemoryStream stream) + { + // Jpeg loses data, so we don't want to use it to serialize... + ImageFormat dest = RawFormat; + if (dest.Guid == ImageFormat.Jpeg.Guid) + dest = ImageFormat.Png; + + // If we don't find an Encoder (for things like Icon), we just switch back to PNG... + ImageCodecInfo codec = dest.FindEncoder() ?? ImageFormat.Png.FindEncoder()!; + + Save(stream, codec, null); + } + + /// + /// Saves this to the specified stream in the specified format. + /// + public void Save(Stream stream, ImageFormat format) + { + ArgumentNullException.ThrowIfNull(format); + + ImageCodecInfo codec = format.FindEncoder()!; + Save(stream, codec, null); + } + + /// + /// Saves this to the specified stream in the specified format. + /// + public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters? encoderParams) + { + ArgumentNullException.ThrowIfNull(stream); + ArgumentNullException.ThrowIfNull(encoder); + + IntPtr encoderParamsMemory = IntPtr.Zero; + + if (encoderParams != null) + { + _rawData = null; + encoderParamsMemory = encoderParams.ConvertToMemory(); + } + + try + { + Guid g = encoder.Clsid; + bool saved = false; + + if (_rawData != null) + { + ImageCodecInfo? rawEncoder = RawFormat.FindEncoder(); + if (rawEncoder != null && rawEncoder.Clsid == g) + { + stream.Write(_rawData, 0, _rawData.Length); + saved = true; + } + } + + if (!saved) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream, makeSeekable: false)); + unsafe + { + Gdip.CheckStatus(Gdip.GdipSaveImageToStream( + new HandleRef(this, nativeImage), + streamWrapper.Ptr, + &g, + new HandleRef(encoderParams, encoderParamsMemory))); + } + } + } + finally + { + if (encoderParamsMemory != IntPtr.Zero) + { + Marshal.FreeHGlobal(encoderParamsMemory); + } + } + } + + /// + /// Adds an to this . + /// + public void SaveAdd(EncoderParameters? encoderParams) + { + IntPtr encoder = IntPtr.Zero; + if (encoderParams != null) + encoder = encoderParams.ConvertToMemory(); + + _rawData = null; + + try + { + Gdip.CheckStatus(Gdip.GdipSaveAdd(new HandleRef(this, nativeImage), new HandleRef(encoderParams, encoder))); + } + finally + { + if (encoder != IntPtr.Zero) + { + Marshal.FreeHGlobal(encoder); + } + } + } + + /// + /// Adds an to the specified . + /// + public void SaveAdd(Image image, EncoderParameters? encoderParams) + { + ArgumentNullException.ThrowIfNull(image); + + IntPtr encoder = IntPtr.Zero; + + if (encoderParams != null) + encoder = encoderParams.ConvertToMemory(); + + _rawData = null; + + try + { + Gdip.CheckStatus(Gdip.GdipSaveAddImage( + new HandleRef(this, nativeImage), + new HandleRef(image, image.nativeImage), + new HandleRef(encoderParams, encoder))); + } + finally + { + if (encoder != IntPtr.Zero) + { + Marshal.FreeHGlobal(encoder); + } + } + } + + private static void ThrowIfDirectoryDoesntExist(string filename) + { + var directoryPart = System.IO.Path.GetDirectoryName(filename); + if (!string.IsNullOrEmpty(directoryPart) && !System.IO.Directory.Exists(directoryPart)) + { + throw new DirectoryNotFoundException(SR.Format(SR.TargetDirectoryDoesNotExist, directoryPart, filename)); + } + } + + /// + /// Gets the width and height of this . + /// + public SizeF PhysicalDimension + { + get + { + float width; + float height; + + int status = Gdip.GdipGetImageDimension(new HandleRef(this, nativeImage), out width, out height); + Gdip.CheckStatus(status); + + return new SizeF(width, height); + } + } + + /// + /// Gets the width and height of this . + /// + public Size Size => new Size(Width, Height); + + /// + /// Gets the width of this . + /// + [DefaultValue(false)] + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int Width + { + get + { + int width; + + int status = Gdip.GdipGetImageWidth(new HandleRef(this, nativeImage), out width); + Gdip.CheckStatus(status); + + return width; + } + } + + /// + /// Gets the height of this . + /// + [DefaultValue(false)] + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public int Height + { + get + { + int height; + + int status = Gdip.GdipGetImageHeight(new HandleRef(this, nativeImage), out height); + Gdip.CheckStatus(status); + + return height; + } + } + + /// + /// Gets the horizontal resolution, in pixels-per-inch, of this . + /// + public float HorizontalResolution + { + get + { + float horzRes; + + int status = Gdip.GdipGetImageHorizontalResolution(new HandleRef(this, nativeImage), out horzRes); + Gdip.CheckStatus(status); + + return horzRes; + } + } + + /// + /// Gets the vertical resolution, in pixels-per-inch, of this . + /// + public float VerticalResolution + { + get + { + float vertRes; + + int status = Gdip.GdipGetImageVerticalResolution(new HandleRef(this, nativeImage), out vertRes); + Gdip.CheckStatus(status); + + return vertRes; + } + } + + /// + /// Gets attribute flags for this . + /// + [Browsable(false)] + public int Flags + { + get + { + int flags; + + int status = Gdip.GdipGetImageFlags(new HandleRef(this, nativeImage), out flags); + Gdip.CheckStatus(status); + + return flags; + } + } + + /// + /// Gets the format of this . + /// + public ImageFormat RawFormat + { + get + { + Guid guid = default; + + int status = Gdip.GdipGetImageRawFormat(new HandleRef(this, nativeImage), ref guid); + Gdip.CheckStatus(status); + + return new ImageFormat(guid); + } + } + + /// + /// Gets the pixel format for this . + /// + public PixelFormat PixelFormat + { + get + { + int status = Gdip.GdipGetImagePixelFormat(new HandleRef(this, nativeImage), out PixelFormat format); + return (status != Gdip.Ok) ? PixelFormat.Undefined : format; + } + } + + /// + /// Gets an array of the property IDs stored in this . + /// + [Browsable(false)] + public unsafe int[] PropertyIdList + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPropertyCount(new HandleRef(this, nativeImage), out uint count)); + if (count == 0) + return Array.Empty(); + + var propid = new int[count]; + fixed (int* pPropid = propid) + { + Gdip.CheckStatus(Gdip.GdipGetPropertyIdList(new HandleRef(this, nativeImage), count, pPropid)); + } + + return propid; + } + } + + /// + /// Gets an array of objects that describe this . + /// + [Browsable(false)] + public unsafe PropertyItem[] PropertyItems + { + get + { + Gdip.CheckStatus(Gdip.GdipGetPropertySize(new HandleRef(this, nativeImage), out uint size, out uint count)); + + if (size == 0 || count == 0) + return Array.Empty(); + + var result = new PropertyItem[(int)count]; + byte[] buffer = ArrayPool.Shared.Rent((int)size); + fixed (byte *pBuffer = buffer) + { + PropertyItemInternal* pPropData = (PropertyItemInternal*)pBuffer; + Gdip.CheckStatus(Gdip.GdipGetAllPropertyItems(new HandleRef(this, nativeImage), size, count, pPropData)); + + for (int i = 0; i < count; i++) + { + result[i] = new PropertyItem + { + Id = pPropData[i].id, + Len = pPropData[i].len, + Type = pPropData[i].type, + Value = pPropData[i].Value.ToArray() + }; + } + } + + ArrayPool.Shared.Return(buffer); + return result; + } + } + + /// + /// Gets a bounding rectangle in the specified units for this . + /// + public RectangleF GetBounds(ref GraphicsUnit pageUnit) + { + Gdip.CheckStatus(Gdip.GdipGetImageBounds(new HandleRef(this, nativeImage), out RectangleF bounds, out pageUnit)); + return bounds; + } + + /// + /// Gets or sets the color palette used for this . + /// + [Browsable(false)] + public ColorPalette Palette + { + get + { + Gdip.CheckStatus(Gdip.GdipGetImagePaletteSize(new HandleRef(this, nativeImage), out int size)); + + // "size" is total byte size: + // sizeof(ColorPalette) + (pal->Count-1)*sizeof(ARGB) + + ColorPalette palette = new ColorPalette(size); + + // Memory layout is: + // UINT Flags + // UINT Count + // ARGB Entries[size] + + IntPtr memory = Marshal.AllocHGlobal(size); + try + { + Gdip.CheckStatus(Gdip.GdipGetImagePalette(new HandleRef(this, nativeImage), memory, size)); + palette.ConvertFromMemory(memory); + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return palette; + } + set + { + IntPtr memory = value.ConvertToMemory(); + + try + { + Gdip.CheckStatus(Gdip.GdipSetImagePalette(new HandleRef(this, nativeImage), memory)); + } + finally + { + if (memory != IntPtr.Zero) + { + Marshal.FreeHGlobal(memory); + } + } + } + } + + // Thumbnail support + + /// + /// Returns the thumbnail for this . + /// + public Image GetThumbnailImage(int thumbWidth, int thumbHeight, GetThumbnailImageAbort? callback, IntPtr callbackData) + { + IntPtr thumbImage; + + Gdip.CheckStatus(Gdip.GdipGetImageThumbnail( + new HandleRef(this, nativeImage), + thumbWidth, + thumbHeight, + out thumbImage, + callback, + callbackData)); + + return CreateImageObject(thumbImage); + } + + internal static void ValidateImage(IntPtr image) + { + try + { + Gdip.CheckStatus(Gdip.GdipImageForceValidation(image)); + } + catch + { + Gdip.GdipDisposeImage(image); + throw; + } + } + + /// + /// Returns the number of frames of the given dimension. + /// + public int GetFrameCount(FrameDimension dimension) + { + Guid dimensionID = dimension.Guid; + Gdip.CheckStatus(Gdip.GdipImageGetFrameCount(new HandleRef(this, nativeImage), ref dimensionID, out int count)); + return count; + } + + /// + /// Gets the specified property item from this . + /// + public unsafe PropertyItem? GetPropertyItem(int propid) + { + Gdip.CheckStatus(Gdip.GdipGetPropertyItemSize(new HandleRef(this, nativeImage), propid, out uint size)); + + if (size == 0) + return null; + + PropertyItem result; + byte[] buffer = ArrayPool.Shared.Rent((int)size); + fixed (byte *pBuffer = buffer) + { + PropertyItemInternal* pPropData = (PropertyItemInternal*)pBuffer; + Gdip.CheckStatus(Gdip.GdipGetPropertyItem(new HandleRef(this, nativeImage), propid, size, pPropData)); + + result = new PropertyItem + { + Id = pPropData->id, + Len = pPropData->len, + Type = pPropData->type, + Value = pPropData->Value.ToArray() + }; + } + + ArrayPool.Shared.Return(buffer); + return result; + } + + /// + /// Selects the frame specified by the given dimension and index. + /// + public int SelectActiveFrame(FrameDimension dimension, int frameIndex) + { + Guid dimensionID = dimension.Guid; + Gdip.CheckStatus(Gdip.GdipImageSelectActiveFrame(new HandleRef(this, nativeImage), ref dimensionID, frameIndex)); + return 0; + } + + /// + /// Sets the specified property item to the specified value. + /// + public unsafe void SetPropertyItem(PropertyItem propitem) + { + fixed (byte *propItemValue = propitem.Value) + { + var propItemInternal = new PropertyItemInternal + { + id = propitem.Id, + len = propitem.Len, + type = propitem.Type, + value = propItemValue + }; + Gdip.CheckStatus(Gdip.GdipSetPropertyItem(new HandleRef(this, nativeImage), &propItemInternal)); + } + } + + public void RotateFlip(RotateFlipType rotateFlipType) + { + int status = Gdip.GdipImageRotateFlip(new HandleRef(this, nativeImage), unchecked((int)rotateFlipType)); + Gdip.CheckStatus(status); + } + + /// + /// Removes the specified property item from this . + /// + public void RemovePropertyItem(int propid) + { + int status = Gdip.GdipRemovePropertyItem(new HandleRef(this, nativeImage), propid); + Gdip.CheckStatus(status); + } + + /// + /// Returns information about the codecs used for this . + /// + public EncoderParameters? GetEncoderParameterList(Guid encoder) + { + EncoderParameters p; + + Gdip.CheckStatus(Gdip.GdipGetEncoderParameterListSize( + new HandleRef(this, nativeImage), + ref encoder, + out int size)); + + if (size <= 0) + return null; + + IntPtr buffer = Marshal.AllocHGlobal(size); + try + { + Gdip.CheckStatus(Gdip.GdipGetEncoderParameterList( + new HandleRef(this, nativeImage), + ref encoder, + size, + buffer)); + + p = EncoderParameters.ConvertFromMemory(buffer); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + + return p; + } + + /// + /// Creates a from a Windows handle. + /// + public static Bitmap FromHbitmap(IntPtr hbitmap) => FromHbitmap(hbitmap, IntPtr.Zero); + + /// + /// Creates a from the specified Windows handle with the specified color palette. + /// + public static Bitmap FromHbitmap(IntPtr hbitmap, IntPtr hpalette) + { + Gdip.CheckStatus(Gdip.GdipCreateBitmapFromHBITMAP(hbitmap, hpalette, out IntPtr bitmap)); + return new Bitmap(bitmap); + } + + /// + /// Returns a value indicating whether the pixel format is extended. + /// + public static bool IsExtendedPixelFormat(PixelFormat pixfmt) + { + return (pixfmt & PixelFormat.Extended) != 0; + } + + /// + /// Returns a value indicating whether the pixel format is canonical. + /// + public static bool IsCanonicalPixelFormat(PixelFormat pixfmt) + { + // Canonical formats: + // + // PixelFormat32bppARGB + // PixelFormat32bppPARGB + // PixelFormat64bppARGB + // PixelFormat64bppPARGB + + return (pixfmt & PixelFormat.Canonical) != 0; + } + + internal void SetNativeImage(IntPtr handle) + { + if (handle == IntPtr.Zero) + throw new ArgumentException(SR.NativeHandle0, nameof(handle)); + + nativeImage = handle; + } + + // Multi-frame support + + /// + /// Gets an array of GUIDs that represent the dimensions of frames within this . + /// + [Browsable(false)] + public unsafe Guid[] FrameDimensionsList + { + get + { + Gdip.CheckStatus(Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(this, nativeImage), out int count)); + + Debug.Assert(count >= 0, "FrameDimensionsList returns bad count"); + if (count <= 0) + return Array.Empty(); + + Guid[] guids = new Guid[count]; + fixed (Guid* g = guids) + { + Gdip.CheckStatus(Gdip.GdipImageGetFrameDimensionsList(new HandleRef(this, nativeImage), g, count)); + } + + return guids; + } + } + + /// + /// Returns the size of the specified pixel format. + /// + public static int GetPixelFormatSize(PixelFormat pixfmt) + { + return (unchecked((int)pixfmt) >> 8) & 0xFF; + } + + /// + /// Returns a value indicating whether the pixel format contains alpha information. + /// + public static bool IsAlphaPixelFormat(PixelFormat pixfmt) + { + return (pixfmt & PixelFormat.Alpha) != 0; + } + + internal static Image CreateImageObject(IntPtr nativeImage) + { + Gdip.CheckStatus(Gdip.GdipGetImageType(nativeImage, out int type)); + switch ((ImageType)type) + { + case ImageType.Bitmap: + return new Bitmap(nativeImage); + case ImageType.Metafile: + return new Metafile(nativeImage); + default: + throw new ArgumentException(SR.InvalidImage); + } + } + + internal static unsafe void EnsureSave(Image image, string? filename, Stream? dataStream) + { + if (image.RawFormat.Equals(ImageFormat.Gif)) + { + bool animatedGif = false; + + Gdip.CheckStatus(Gdip.GdipImageGetFrameDimensionsCount(new HandleRef(image, image.nativeImage), out int dimensions)); + if (dimensions <= 0) + { + return; + } + + Span guids = dimensions < 16 ? + stackalloc Guid[dimensions] : + new Guid[dimensions]; + + fixed (Guid* g = &MemoryMarshal.GetReference(guids)) + { + Gdip.CheckStatus(Gdip.GdipImageGetFrameDimensionsList(new HandleRef(image, image.nativeImage), g, dimensions)); + } + + Guid timeGuid = FrameDimension.Time.Guid; + for (int i = 0; i < dimensions; i++) + { + if (timeGuid == guids[i]) + { + animatedGif = image.GetFrameCount(FrameDimension.Time) > 1; + break; + } + } + + if (animatedGif) + { + try + { + Stream? created = null; + long lastPos = 0; + if (dataStream != null) + { + lastPos = dataStream.Position; + dataStream.Position = 0; + } + + try + { + if (dataStream == null) + { + created = dataStream = File.OpenRead(filename!); + } + + image._rawData = new byte[(int)dataStream.Length]; + dataStream.Read(image._rawData, 0, (int)dataStream.Length); + } + finally + { + if (created != null) + { + created.Close(); + } + else + { + dataStream!.Position = lastPos; + } + } + } + // possible exceptions for reading the filename + catch (UnauthorizedAccessException) + { + } + catch (DirectoryNotFoundException) + { + } + catch (IOException) + { + } + // possible exceptions for setting/getting the position inside dataStream + catch (NotSupportedException) + { + } + catch (ObjectDisposedException) + { + } + // possible exception when reading stuff into dataStream + catch (ArgumentException) + { + } + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs b/src/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs new file mode 100644 index 00000000000..e0b89339d4d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ImageAnimator.cs @@ -0,0 +1,426 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Imaging; +using System.Threading; + +namespace System.Drawing +{ + /// + /// Animates one or more images that have time-based frames. + /// See the ImageInfo.cs file for the helper nested ImageInfo class. + /// + /// A common pattern for using this class is as follows (See PictureBox control): + /// 1. The winform app (user's code) calls ImageAnimator.Animate() from the main thread. + /// 2. Animate() spawns the animating (worker) thread in the background, which will update the image + /// frames and raise the OnFrameChanged event, which handler will be executed in the main thread. + /// 3. The main thread triggers a paint event (Invalidate()) from the OnFrameChanged handler. + /// 4. From the OnPaint event, the main thread calls ImageAnimator.UpdateFrames() and then paints the + /// image (updated frame). + /// 5. The main thread calls ImageAnimator.StopAnimate() when needed. This does not kill the worker thread. + /// + /// Comment on locking the image ref: + /// We need to synchronize access to sections of code that modify the image(s), but we don't want to block + /// animation of one image when modifying a different one; for this, we use the image ref for locking the + /// critical section (lock(image)). + /// + /// This class is safe for multi-threading but Image is not; multithreaded applications must use a critical + /// section lock using the image ref the image access is not from the same thread that executes ImageAnimator + /// code. If the user code locks on the image ref forever a deadlock will happen preventing the animation + /// from occurring. + /// + public sealed partial class ImageAnimator + { + // We use a timer to apply an animation tick speeds of something a bit shorter than 50ms + // such that if the requested frame rate is about 20 frames per second, we will rarely skip + // a frame entirely. Sometimes we'll show a few more frames if available, but we will never + // show more than 25 frames a second and that's OK. + internal const int AnimationDelayMS = 40; + + /// + /// A list of images to be animated. + /// + private static List? s_imageInfoList; + + /// + /// A variable to flag when an image or images need to be updated due to the selection of a new frame + /// in an image. We don't need to synchronize access to this variable, in the case it is true we don't + /// do anything, otherwise the worse case is where a thread attempts to update the image's frame after + /// another one did which is harmless. + /// + private static bool s_anyFrameDirty; + + /// + /// The thread used for animating the images. + /// + private static Thread? s_animationThread; + + /// + /// Lock that allows either concurrent read-access to the images list for multiple threads, or write- + /// access to it for a single thread. Observe that synchronization access to image objects are done + /// with critical sections (lock). + /// + private static readonly ReaderWriterLock s_rwImgListLock = new ReaderWriterLock(); + + /// + /// Flag to avoid a deadlock when waiting on a write-lock and an attempt to acquire a read-lock is + /// made in the same thread. If RWLock is currently owned by another thread, the current thread is going to wait on an + /// event using CoWaitForMultipleHandles while pumps message. + /// The comment above refers to the COM STA message pump, not to be confused with the UI message pump. + /// However, the effect is the same, the COM message pump will pump messages and dispatch them to the + /// window while waiting on the writer lock; this has the potential of creating a re-entrancy situation + /// that if during the message processing a wait on a reader lock is originated the thread will be block + /// on itself. + /// While processing STA message, the thread may call back into managed code. We do this because + /// we can not block finalizer thread. Finalizer thread may need to release STA objects on this thread. If + /// the current thread does not pump message, finalizer thread is blocked, and AD unload is blocked while + /// waiting for finalizer thread. RWLock is a fair lock. If a thread waits for a writer lock, then it needs + /// a reader lock while pumping message, the thread is blocked forever. + /// This TLS variable is used to flag the above situation and avoid the deadlock, it is ThreadStatic so each + /// thread calling into ImageAnimator is guarded against this problem. + /// + + + + + [ThreadStatic] + private static int t_threadWriterLockWaitCount; + + /// + /// Prevent instantiation of this class. + /// + private ImageAnimator() + { + } + + /// + /// Advances the frame in the specified image. The new frame is drawn the next time the image is rendered. + /// + public static void UpdateFrames(Image? image) + { + if (image == null || s_imageInfoList == null) + { + return; + } + + if (t_threadWriterLockWaitCount > 0) + { + // Cannot acquire reader lock - frame update will be missed. + return; + } + + // If the current thread already has the writer lock, no reader lock is acquired. Instead, the lock count on + // the writer lock is incremented. It already has a reader lock, the locks ref count will be incremented + // w/o placing the request at the end of the reader queue. + + s_rwImgListLock.AcquireReaderLock(Timeout.Infinite); + + try + { + bool foundDirty = false; + bool foundImage = false; + + foreach (ImageInfo imageInfo in s_imageInfoList) + { + if (imageInfo.Image == image) + { + if (imageInfo.FrameDirty) + { + // See comment in the class header about locking the image ref. + lock (imageInfo.Image) + { + imageInfo.UpdateFrame(); + } + } + + foundImage = true; + } + else if (imageInfo.FrameDirty) + { + foundDirty = true; + } + + if (foundDirty && foundImage) + { + break; + } + } + + s_anyFrameDirty = foundDirty; + } + finally + { + s_rwImgListLock.ReleaseReaderLock(); + } + } + + /// + /// Advances the frame in all images currently being animated. The new frame is drawn the next time the image is rendered. + /// + public static void UpdateFrames() + { + if (!s_anyFrameDirty || s_imageInfoList == null) + { + return; + } + + if (t_threadWriterLockWaitCount > 0) + { + // Cannot acquire reader lock at this time, frames update will be missed. + return; + } + + s_rwImgListLock.AcquireReaderLock(Timeout.Infinite); + + try + { + foreach (ImageInfo imageInfo in s_imageInfoList) + { + // See comment in the class header about locking the image ref. + lock (imageInfo.Image) + { + imageInfo.UpdateFrame(); + } + } + + s_anyFrameDirty = false; + } + finally + { + s_rwImgListLock.ReleaseReaderLock(); + } + } + + /// + /// Adds an image to the image manager. If the image does not support animation this method does nothing. + /// This method creates the image list and spawns the animation thread the first time it is called. + /// + public static void Animate(Image image, EventHandler onFrameChangedHandler) + { + if (image == null) + { + return; + } + + ImageInfo? imageInfo = null; + + // See comment in the class header about locking the image ref. + lock (image) + { + // could we avoid creating an ImageInfo object if FrameCount == 1 ? + imageInfo = new ImageInfo(image); + } + + // If the image is already animating, stop animating it + StopAnimate(image, onFrameChangedHandler); + + // Acquire a writer lock to modify the image info list. If the thread has a reader lock we need to upgrade + // it to a writer lock; acquiring a reader lock in this case would block the thread on itself. + // If the thread already has a writer lock its ref count will be incremented w/o placing the request in the + // writer queue. See ReaderWriterLock.AcquireWriterLock method in the MSDN. + + bool readerLockHeld = s_rwImgListLock.IsReaderLockHeld; + LockCookie lockDowngradeCookie = default; + + t_threadWriterLockWaitCount++; + + try + { + if (readerLockHeld) + { + lockDowngradeCookie = s_rwImgListLock.UpgradeToWriterLock(Timeout.Infinite); + } + else + { + s_rwImgListLock.AcquireWriterLock(Timeout.Infinite); + } + } + finally + { + t_threadWriterLockWaitCount--; + Debug.Assert(t_threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero."); + } + + try + { + if (imageInfo.Animated) + { + // Construct the image array + // + s_imageInfoList ??= new List(); + + // Add the new image + // + imageInfo.FrameChangedHandler = onFrameChangedHandler; + s_imageInfoList.Add(imageInfo); + + // Construct a new timer thread if we haven't already + // + if (s_animationThread == null) + { + s_animationThread = new Thread(new ThreadStart(AnimateImages)); + s_animationThread.Name = nameof(ImageAnimator); + s_animationThread.IsBackground = true; + s_animationThread.Start(); + } + } + } + finally + { + if (readerLockHeld) + { + s_rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie); + } + else + { + s_rwImgListLock.ReleaseWriterLock(); + } + } + } + + /// + /// Whether or not the image has multiple time-based frames. + /// + public static bool CanAnimate([NotNullWhen(true)] Image? image) + { + if (image == null) + { + return false; + } + + // See comment in the class header about locking the image ref. + lock (image) + { + Guid[] dimensions = image.FrameDimensionsList; + + foreach (Guid guid in dimensions) + { + FrameDimension dimension = new FrameDimension(guid); + if (dimension.Equals(FrameDimension.Time)) + { + return image.GetFrameCount(FrameDimension.Time) > 1; + } + } + } + + return false; + } + + /// + /// Removes an image from the image manager so it is no longer animated. + /// + public static void StopAnimate(Image image, EventHandler onFrameChangedHandler) + { + // Make sure we have a list of images + if (image == null || s_imageInfoList == null) + { + return; + } + + // Acquire a writer lock to modify the image info list - See comments on Animate() about this locking. + + bool readerLockHeld = s_rwImgListLock.IsReaderLockHeld; + LockCookie lockDowngradeCookie = default; + + t_threadWriterLockWaitCount++; + + try + { + if (readerLockHeld) + { + lockDowngradeCookie = s_rwImgListLock.UpgradeToWriterLock(Timeout.Infinite); + } + else + { + s_rwImgListLock.AcquireWriterLock(Timeout.Infinite); + } + } + finally + { + t_threadWriterLockWaitCount--; + Debug.Assert(t_threadWriterLockWaitCount >= 0, "threadWriterLockWaitCount less than zero."); + } + + try + { + // Find the corresponding reference and remove it + for (int i = 0; i < s_imageInfoList.Count; i++) + { + ImageInfo imageInfo = s_imageInfoList[i]; + + if (image == imageInfo.Image) + { + if ((onFrameChangedHandler == imageInfo.FrameChangedHandler) || (onFrameChangedHandler != null && onFrameChangedHandler.Equals(imageInfo.FrameChangedHandler))) + { + s_imageInfoList.Remove(imageInfo); + } + break; + } + } + } + finally + { + if (readerLockHeld) + { + s_rwImgListLock.DowngradeFromWriterLock(ref lockDowngradeCookie); + } + else + { + s_rwImgListLock.ReleaseWriterLock(); + } + } + } + + /// + /// Worker thread procedure which implements the main animation loop. + /// NOTE: This is the ONLY code the worker thread executes, keeping it in one method helps better understand + /// any synchronization issues. + /// WARNING: Also, this is the only place where ImageInfo objects (not the contained image object) are modified, + /// so no access synchronization is required to modify them. + /// + private static void AnimateImages() + { + Debug.Assert(s_imageInfoList != null, "Null images list"); + + Stopwatch stopwatch = Stopwatch.StartNew(); + + while (true) + { + Thread.Sleep(AnimationDelayMS); + + // Because Thread.Sleep is not accurate, capture how much time has actually elapsed during the animation + long timeElapsed = stopwatch.ElapsedMilliseconds; + stopwatch.Restart(); + + // Acquire reader-lock to access imageInfoList, elements in the list can be modified w/o needing a writer-lock. + // Observe that we don't need to check if the thread is waiting or a writer lock here since the thread this + // method runs in never acquires a writer lock. + s_rwImgListLock.AcquireReaderLock(Timeout.Infinite); + try + { + for (int i = 0; i < s_imageInfoList.Count; i++) + { + ImageInfo imageInfo = s_imageInfoList[i]; + + if (imageInfo.Animated) + { + imageInfo.AdvanceAnimationBy(timeElapsed); + + if (imageInfo.FrameDirty) + { + s_anyFrameDirty = true; + } + } + } + } + finally + { + s_rwImgListLock.ReleaseReaderLock(); + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs b/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs new file mode 100644 index 00000000000..66a00b47edb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs @@ -0,0 +1,164 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers.Binary; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; + +namespace System.Drawing +{ + public class ImageConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type? sourceType) + { + return sourceType == typeof(byte[]) || sourceType == typeof(Icon); + } + + public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) + { + return destinationType == typeof(byte[]) || destinationType == typeof(string); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + if (value is Icon icon) + { + return icon.ToBitmap(); + } + + if (value is byte[] bytes) + { + Debug.Assert(value != null, "value is null."); + // Try to get memory stream for images with ole header. + MemoryStream memStream = GetBitmapStream(bytes) ?? new MemoryStream(bytes); + return Image.FromStream(memStream); + } + else + { + return base.ConvertFrom(context, culture, value); + } + } + + public override object ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (destinationType == typeof(string)) + { + if (value == null) + { + return SR.none; + } + else if (value is Image) + { + return value.ToString()!; + } + } + else if (destinationType == typeof(byte[])) + { + if (value == null) + { + return Array.Empty(); + } + else if (value is Image image) + { + using (MemoryStream ms = new MemoryStream()) + { + ImageFormat dest = image.RawFormat; + // Jpeg loses data, so we don't want to use it to serialize. + if (dest == ImageFormat.Jpeg) + { + dest = ImageFormat.Png; + } + + // If we don't find an Encoder (for things like Icon), we + // just switch back to PNG. + ImageCodecInfo codec = FindEncoder(dest) ?? FindEncoder(ImageFormat.Png)!; + image.Save(ms, codec, null); + return ms.ToArray(); + } + } + } + + throw GetConvertFromException(value); + } + + // Find any random encoder which supports this format. + private static ImageCodecInfo? FindEncoder(ImageFormat imageformat) + { + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); + foreach (ImageCodecInfo codec in codecs) + { + if (codec.FormatID.Equals(imageformat.Guid)) + return codec; + } + return null; + } + + [RequiresUnreferencedCode("The Type of value cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type.")] + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext? context, object? value, Attribute[]? attributes) + { + return TypeDescriptor.GetProperties(typeof(Image), attributes); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext? context) => true; + + private static unsafe MemoryStream? GetBitmapStream(ReadOnlySpan rawData) + { + try + { + short signature = BinaryPrimitives.ReadInt16LittleEndian(rawData); + + if (signature != 0x1c15) + { + return null; + } + + // The data is in the form of OBJECTHEADER. It's an encoded format that Access uses to push images into the DB. + // + // The layout of OBJECTHEADER is as follows - we only need the signature + // and headersize fields, which need to be read as little-endian data: + // + // [StructLayout(LayoutKind.Sequential)] + // private struct OBJECTHEADER + // { + // public short signature; // it's always 0x1c15 + // public short headersize; + // public short objectType; + // public short nameLen; + // public short classLen; + // public short nameOffset; + // public short classOffset; + // public short width; + // public short height; + // public IntPtr pInfo; + // } + short headersize = BinaryPrimitives.ReadInt16LittleEndian(rawData.Slice(2, 2)); + + // pHeader.signature will always be 0x1c15. + // "PBrush" should be the 6 chars after position 12 as well. + if (rawData.Length <= headersize + 18 || + !rawData.Slice(headersize + 12, 6).SequenceEqual("PBrush"u8)) + { + return null; + } + + // We can safely trust that we've got a bitmap. + // The start of our bitmap data in the rawdata is always 78. + return new MemoryStream(rawData.Slice(78).ToArray()); + } + catch (OutOfMemoryException) // This exception may be caused by creating a new MemoryStream. + { + } + catch (ArgumentOutOfRangeException) // This exception may get thrown by MemoryMarshal when input array size is less than the size of the output type. + { + } + + return null; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs b/src/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs new file mode 100644 index 00000000000..9bbb729a629 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ImageFormatConverter.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Imaging; +using System.Globalization; +using System.Reflection; + +namespace System.Drawing +{ + [SuppressMessage("Interoperability", "CA1416:Validate platform compatibility", Justification = "Heif and Webp are referenced here for " + + "design-time support, the user is responsible to ensure that they are used on a supported version of Windows.")] + public class ImageFormatConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + } + + public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) + { + if ((destinationType == typeof(string)) || (destinationType == typeof(InstanceDescriptor))) + { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + // we must be able to convert from short names and long names + string? strFormat = value as string; + if (strFormat == null) + { + // case #1, this is not a string + return base.ConvertFrom(context, culture, value); + } + + if ((strFormat[0] == '[') && (strFormat.Length >= 50) && Guid.TryParse(strFormat.AsSpan(14, 36), out Guid guid)) + { + // case #2, this is probably a long format (guid) + return new ImageFormat(guid); + } + + // case #3, this is probably a short format + if (strFormat.Equals("Bmp", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Bmp; + else if (strFormat.Equals("Emf", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Emf; + else if (strFormat.Equals("Exif", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Exif; + else if (strFormat.Equals("Gif", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Gif; + else if (strFormat.Equals("Icon", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Icon; + else if (strFormat.Equals("Jpeg", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Jpeg; + else if (strFormat.Equals("MemoryBmp", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.MemoryBmp; + else if (strFormat.Equals("Png", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Png; + else if (strFormat.Equals("Tiff", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Tiff; + else if (strFormat.Equals("Wmf", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Wmf; + else if (strFormat.Equals("Heif", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Heif; + else if (strFormat.Equals("Webp", StringComparison.OrdinalIgnoreCase)) + return ImageFormat.Webp; + + throw new FormatException(SR.Format(SR.ConvertInvalidPrimitive, strFormat, nameof(ImageFormat))); + } + + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + if (value is ImageFormat imgFormat) + { + if (destinationType == typeof(string)) + { + return imgFormat.ToString(); + } + + if (destinationType == typeof(InstanceDescriptor)) + { + string? strFormat = null; + if (imgFormat.Guid.Equals(ImageFormat.Bmp.Guid)) + strFormat = "Bmp"; + else if (imgFormat.Guid.Equals(ImageFormat.Emf.Guid)) + strFormat = "Emf"; + else if (imgFormat.Guid.Equals(ImageFormat.Exif.Guid)) + strFormat = "Exif"; + else if (imgFormat.Guid.Equals(ImageFormat.Gif.Guid)) + strFormat = "Gif"; + else if (imgFormat.Guid.Equals(ImageFormat.Icon.Guid)) + strFormat = "Icon"; + else if (imgFormat.Guid.Equals(ImageFormat.Jpeg.Guid)) + strFormat = "Jpeg"; + else if (imgFormat.Guid.Equals(ImageFormat.MemoryBmp.Guid)) + strFormat = "MemoryBmp"; + else if (imgFormat.Guid.Equals(ImageFormat.Png.Guid)) + strFormat = "Png"; + else if (imgFormat.Guid.Equals(ImageFormat.Tiff.Guid)) + strFormat = "Tiff"; + else if (imgFormat.Guid.Equals(ImageFormat.Wmf.Guid)) + strFormat = "Wmf"; + + if (strFormat != null) + { + return new InstanceDescriptor(typeof(ImageFormat).GetProperty(strFormat), null); + } + else + { + ConstructorInfo? ctor = typeof(ImageFormat).GetConstructor(new Type[] { typeof(Guid) }); + return new InstanceDescriptor(ctor, new object[] { imgFormat.Guid }); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext? context) + { + return new TypeConverter.StandardValuesCollection(new ImageFormat[] + { + ImageFormat.MemoryBmp, + ImageFormat.Bmp, + ImageFormat.Emf, + ImageFormat.Wmf, + ImageFormat.Gif, + ImageFormat.Jpeg, + ImageFormat.Png, + ImageFormat.Tiff, + ImageFormat.Exif, + ImageFormat.Icon, + ImageFormat.Heif, + ImageFormat.Webp + }); + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) => true; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ImageInfo.cs b/src/System.Drawing.Common/src/System/Drawing/ImageInfo.cs new file mode 100644 index 00000000000..9eb32d87300 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ImageInfo.cs @@ -0,0 +1,224 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Imaging; + +namespace System.Drawing +{ + /// + /// Animates one or more images that have time-based frames. This file contains the nested ImageInfo class + /// - See ImageAnimator.cs for the definition of the outer class. + /// + public sealed partial class ImageAnimator + { + /// + /// ImageAnimator nested helper class used to store extra image state info. + /// + private sealed class ImageInfo + { + private const int PropertyTagFrameDelay = 0x5100; + private const int PropertyTagLoopCount = 0x5101; + + private readonly Image _image; + private int _frame; + private short _loop; + private readonly int _frameCount; + private readonly short _loopCount; + private bool _frameDirty; + private readonly bool _animated; + private EventHandler? _onFrameChangedHandler; + private readonly long[]? _frameEndTimes; + private readonly long _totalAnimationTime; + private long _frameTimer; + + public ImageInfo(Image image) + { + _image = image; + _animated = ImageAnimator.CanAnimate(image); + _frameEndTimes = null; + + if (_animated) + { + _frameCount = image.GetFrameCount(FrameDimension.Time); + + PropertyItem? frameDelayItem = image.GetPropertyItem(PropertyTagFrameDelay); + + // If the image does not have a frame delay, we just return 0. + if (frameDelayItem != null) + { + // Convert the frame delay from byte[] to int + byte[] values = frameDelayItem.Value!; + + // On Windows, we get the frame delays for every frame. On Linux, we only get the first frame delay. + // We handle this by treating the frame delays as a repeating sequence, asserting that the sequence + // is fully repeatable to match the frame count. + Debug.Assert(values.Length % 4 == 0, "PropertyItem has an invalid value byte array. It should have a length evenly divisible by 4 to represent ints."); + Debug.Assert(_frameCount % (values.Length / 4) == 0, "PropertyItem has invalid value byte array. The FrameCount should be evenly divisible by a quarter of the byte array's length."); + + _frameEndTimes = new long[_frameCount]; + long lastEndTime = 0; + + for (int f = 0, i = 0; f < _frameCount; ++f, i += 4) + { + if (i >= values.Length) + { + i = 0; + } + + // Frame delays are stored in 1/100ths of a second; convert to milliseconds while accumulating + // Per spec, a frame delay can be 0 which is treated as a single animation tick + int delay = BitConverter.ToInt32(values, i) * 10; + lastEndTime += delay > 0 ? delay : ImageAnimator.AnimationDelayMS; + + // Guard against overflows + if (lastEndTime < _totalAnimationTime) + { + lastEndTime = _totalAnimationTime; + } + else + { + _totalAnimationTime = lastEndTime; + } + + _frameEndTimes[f] = lastEndTime; + } + } + + PropertyItem? loopCountItem = image.GetPropertyItem(PropertyTagLoopCount); + + if (loopCountItem != null) + { + // The loop count is a short where 0 = infinite, and a positive value indicates the + // number of times to loop. The animation will be shown 1 time more than the loop count. + byte[] values = loopCountItem.Value!; + + Debug.Assert(values.Length == sizeof(short), "PropertyItem has an invalid byte array. It should represent a single short value."); + _loopCount = BitConverter.ToInt16(values); + } + else + { + _loopCount = 0; + } + } + else + { + _frameCount = 1; + } + } + + /// + /// Whether the image supports animation. + /// + public bool Animated => _animated; + + /// + /// The current frame has changed but the image has not yet been updated. + /// + public bool FrameDirty => _frameDirty; + + public EventHandler? FrameChangedHandler + { + get + { + return _onFrameChangedHandler; + } + set + { + _onFrameChangedHandler = value; + } + } + + /// + /// The total animation time of the image in milliseconds, or 0 if not animated. + /// + private long TotalAnimationTime => Animated ? _totalAnimationTime : 0; + + /// + /// Whether animation should progress, respecting the image's animation support + /// and if there are animation frames or loops remaining. + /// + private bool ShouldAnimate => TotalAnimationTime > 0 ? (_loopCount == 0 || _loop <= _loopCount) : false; + + /// + /// Advance the animation by the specified number of milliseconds. If the advancement + /// progresses beyond the end time of the current Frame, + /// will be called. Subscribed handlers often use that event to call + /// . + /// + /// If the animation progresses beyond the end of the image's total animation time, + /// the animation will loop. + /// + /// + /// + /// This animation does not respect a GIF's specified number of animation repeats; + /// instead, animations loop indefinitely. + /// + /// The number of milliseconds to advance the animation by + public void AdvanceAnimationBy(long milliseconds) + { + if (ShouldAnimate) + { + int oldFrame = _frame; + _frameTimer += milliseconds; + + if (_frameTimer > TotalAnimationTime) + { + _loop += (short)Math.DivRem(_frameTimer, TotalAnimationTime, out long newTimer); + _frameTimer = newTimer; + + if (!ShouldAnimate) + { + // If we've finished looping, then freeze onto the last frame + _frame = _frameCount - 1; + _frameTimer = TotalAnimationTime; + } + else if (_frame > 0 && _frameTimer < _frameEndTimes![_frame - 1]) + { + // If the loop put us before the current frame (which is common) + // then reset back to the first frame. We will then progress + // forward again from there (below). + _frame = 0; + } + } + + while (_frameTimer > _frameEndTimes![_frame]) + { + _frame++; + } + + if (_frame != oldFrame) + { + _frameDirty = true; + OnFrameChanged(EventArgs.Empty); + } + } + } + + /// + /// The image this object wraps. + /// + internal Image Image => _image; + + /// + /// Selects the current frame as the active frame in the image. + /// + internal void UpdateFrame() + { + if (_frameDirty) + { + _image.SelectActiveFrame(FrameDimension.Time, _frame); + _frameDirty = false; + } + } + + /// + /// Raises the FrameChanged event. + /// + private void OnFrameChanged(EventArgs e) + { + _onFrameChangedHandler?.Invoke(_image, e); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ImageType.cs b/src/System.Drawing.Common/src/System/Drawing/ImageType.cs new file mode 100644 index 00000000000..fa15f3d72bd --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ImageType.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + internal enum ImageType + { + Unknown = 0, + Bitmap = 1, + Metafile = 2, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs new file mode 100644 index 00000000000..f9502ca98e2 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/BitmapData.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the attributes of a bitmap image. + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class BitmapData + { + private int _width; + private int _height; + private int _stride; + private PixelFormat _pixelFormat; + private IntPtr _scan0; + private int _reserved; + + /// + /// Specifies the pixel width of the . + /// + public int Width + { + get { return _width; } + set { _width = value; } + } + + /// + /// Specifies the pixel height of the . + /// + public int Height + { + get { return _height; } + set { _height = value; } + } + + /// + /// Specifies the stride width of the . + /// + public int Stride + { + get { return _stride; } + set { _stride = value; } + } + + /// + /// Specifies the format of the pixel information in this . + /// + public PixelFormat PixelFormat + { + get { return _pixelFormat; } + set + { + switch (value) + { + case PixelFormat.DontCare: + // case PixelFormat.Undefined: same as DontCare + case PixelFormat.Max: + case PixelFormat.Indexed: + case PixelFormat.Gdi: + case PixelFormat.Format16bppRgb555: + case PixelFormat.Format16bppRgb565: + case PixelFormat.Format24bppRgb: + case PixelFormat.Format32bppRgb: + case PixelFormat.Format1bppIndexed: + case PixelFormat.Format4bppIndexed: + case PixelFormat.Format8bppIndexed: + case PixelFormat.Alpha: + case PixelFormat.Format16bppArgb1555: + case PixelFormat.PAlpha: + case PixelFormat.Format32bppPArgb: + case PixelFormat.Extended: + case PixelFormat.Format16bppGrayScale: + case PixelFormat.Format48bppRgb: + case PixelFormat.Format64bppPArgb: + case PixelFormat.Canonical: + case PixelFormat.Format32bppArgb: + case PixelFormat.Format64bppArgb: + break; + default: + throw new System.ComponentModel.InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PixelFormat)); + } + + _pixelFormat = value; + } + } + + /// + /// Specifies the address of the pixel data. + /// + public IntPtr Scan0 + { + get { return _scan0; } + set { _scan0 = value; } + } + + /// + /// Reserved. Do not use. + /// + public int Reserved + { + get { return _reserved; } + set { _reserved = value; } + } + + internal ref int GetPinnableReference() => ref _width; + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(BitmapData), MarshalMode.ManagedToUnmanagedIn, typeof(PinningMarshaller))] + internal static unsafe class PinningMarshaller + { + public static ref int GetPinnableReference(BitmapData managed) => ref (managed is null ? ref Unsafe.NullRef() : ref managed.GetPinnableReference()); + + // All usages in our currently supported scenarios will always go through GetPinnableReference + public static int* ConvertToUnmanaged(BitmapData _) => throw new UnreachableException(); + } +#endif + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorAdjustType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorAdjustType.cs new file mode 100644 index 00000000000..fdda9a30796 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorAdjustType.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies which GDI+ objects use color adjustment information. + /// + public enum ColorAdjustType + { + /// + /// Defines color adjustment information that is used by all GDI+ objects that do not have their own color + /// adjustment information. + /// + Default = 0, + /// + /// Defines color adjustment information for + /// objects. + /// + Bitmap, + /// + /// Defines color adjustment information for objects. + /// + Brush, + /// + /// Defines color adjustment information for objects. + /// + Pen, + /// + /// Defines color adjustment information for text. + /// + Text, + /// + /// Specifies the number of types specified. + /// + Count, + /// + /// Specifies the number of types specified. + /// + Any + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorChannelFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorChannelFlags.cs new file mode 100644 index 00000000000..690112ae464 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorChannelFlags.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies a range of CMYK channels. + /// + public enum ColorChannelFlag + { + /// + /// Specifies the Cyan color channel. + /// + ColorChannelC = 0, + /// + /// Specifies the Magenta color channel. + /// + ColorChannelM, + /// + /// Specifies the Yellow color channel. + /// + ColorChannelY, + /// + /// Specifies the Black color channel. + /// + ColorChannelK, + /// + /// This element specifies to leave the color channel unchanged from the last selected channel. + /// + ColorChannelLast + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMap.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMap.cs new file mode 100644 index 00000000000..2e747c756eb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMap.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Defines a map for converting colors. + /// + public sealed class ColorMap + { + private Color _oldColor; + private Color _newColor; + + /// + /// Initializes a new instance of the class. + /// + public ColorMap() + { + } + + /// + /// Specifies the existing to be converted. + /// + public Color OldColor + { + get { return _oldColor; } + set { _oldColor = value; } + } + /// + /// Specifies the new to which to convert. + /// + public Color NewColor + { + get { return _newColor; } + set { _newColor = value; } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMapType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMapType.cs new file mode 100644 index 00000000000..04ceaf39048 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMapType.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the types of color maps. + /// + public enum ColorMapType + { + /// + /// A default color map. + /// + Default = 0, + /// + /// Specifies a color map for a . + /// + Brush + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs new file mode 100644 index 00000000000..7859deb26cb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrix.cs @@ -0,0 +1,413 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing.Imaging +{ + /// + /// Defines a 5 x 5 matrix that contains the homogeneous coordinates for the RGBA space. + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class ColorMatrix + { + private float _matrix00; + private float _matrix01; + private float _matrix02; + private float _matrix03; + private float _matrix04; + private float _matrix10; + private float _matrix11; + private float _matrix12; + private float _matrix13; + private float _matrix14; + private float _matrix20; + private float _matrix21; + private float _matrix22; + private float _matrix23; + private float _matrix24; + private float _matrix30; + private float _matrix31; + private float _matrix32; + private float _matrix33; + private float _matrix34; + private float _matrix40; + private float _matrix41; + private float _matrix42; + private float _matrix43; + private float _matrix44; + + /// + /// Initializes a new instance of the class. + /// + public ColorMatrix() + { + /* + * Setup identity matrix by default + */ + + _matrix00 = 1.0f; + //matrix01 = 0.0f; + //matrix02 = 0.0f; + //matrix03 = 0.0f; + //matrix04 = 0.0f; + //matrix10 = 0.0f; + _matrix11 = 1.0f; + //matrix12 = 0.0f; + //matrix13 = 0.0f; + //matrix14 = 0.0f; + //matrix20 = 0.0f; + //matrix21 = 0.0f; + _matrix22 = 1.0f; + // matrix23 = 0.0f; + // matrix24 = 0.0f; + // matrix30 = 0.0f; + //matrix31 = 0.0f; + // matrix32 = 0.0f; + _matrix33 = 1.0f; + // matrix34 = 0.0f; + // matrix40 = 0.0f; + // matrix41 = 0.0f; + // matrix42 = 0.0f; + // matrix43 = 0.0f; + _matrix44 = 1.0f; + } + + /// + /// Represents the element at the 0th row and 0th column of this . + /// + public float Matrix00 + { + get { return _matrix00; } + set { _matrix00 = value; } + } + /// + /// Represents the element at the 0th row and 1st column of this . + /// + public float Matrix01 + { + get { return _matrix01; } + set { _matrix01 = value; } + } + + /// + /// Represents the element at the 0th row and 2nd column of this . + /// + public float Matrix02 + { + get { return _matrix02; } + set { _matrix02 = value; } + } + + /// + /// Represents the element at the 0th row and 3rd column of this . + /// + public float Matrix03 + { + get { return _matrix03; } + set { _matrix03 = value; } + } + + /// + /// Represents the element at the 0th row and 4th column of this . + /// + public float Matrix04 + { + get { return _matrix04; } + set { _matrix04 = value; } + } + + /// + /// Represents the element at the 1st row and 0th column of this . + /// + public float Matrix10 + { + get { return _matrix10; } + set { _matrix10 = value; } + } + + /// + /// Represents the element at the 1st row and 1st column of this . + /// + public float Matrix11 + { + get { return _matrix11; } + set { _matrix11 = value; } + } + + /// + /// Represents the element at the 1st row and 2nd column of this . + /// + public float Matrix12 + { + get { return _matrix12; } + set { _matrix12 = value; } + } + + /// + /// Represents the element at the 1st row and 3rd column of this . + /// + public float Matrix13 + { + get { return _matrix13; } + set { _matrix13 = value; } + } + + /// + /// Represents the element at the 1st row and 4th column of this . + /// + public float Matrix14 + { + get { return _matrix14; } + set { _matrix14 = value; } + } + + /// + /// Represents the element at the 2nd row and 0th column of this . + /// + public float Matrix20 + { + get { return _matrix20; } + set { _matrix20 = value; } + } + + /// + /// Represents the element at the 2nd row and 1st column of this . + /// + public float Matrix21 + { + get { return _matrix21; } + set { _matrix21 = value; } + } + + /// + /// Represents the element at the 2nd row and 2nd column of this . + /// + public float Matrix22 + { + get { return _matrix22; } + set { _matrix22 = value; } + } + + /// + /// Represents the element at the 2nd row and 3rd column of this . + /// + public float Matrix23 + { + get { return _matrix23; } + set { _matrix23 = value; } + } + + /// + /// Represents the element at the 2nd row and 4th column of this . + /// + public float Matrix24 + { + get { return _matrix24; } + set { _matrix24 = value; } + } + + /// + /// Represents the element at the 3rd row and 0th column of this . + /// + public float Matrix30 + { + get { return _matrix30; } + set { _matrix30 = value; } + } + + /// + /// Represents the element at the 3rd row and 1st column of this . + /// + public float Matrix31 + { + get { return _matrix31; } + set { _matrix31 = value; } + } + + /// + /// Represents the element at the 3rd row and 2nd column of this . + /// + public float Matrix32 + { + get { return _matrix32; } + set { _matrix32 = value; } + } + + /// + /// Represents the element at the 3rd row and 3rd column of this . + /// + public float Matrix33 + { + get { return _matrix33; } + set { _matrix33 = value; } + } + + /// + /// Represents the element at the 3rd row and 4th column of this . + /// + public float Matrix34 + { + get { return _matrix34; } + set { _matrix34 = value; } + } + + /// + /// Represents the element at the 4th row and 0th column of this . + /// + public float Matrix40 + { + get { return _matrix40; } + set { _matrix40 = value; } + } + + /// + /// Represents the element at the 4th row and 1st column of this . + /// + public float Matrix41 + { + get { return _matrix41; } + set { _matrix41 = value; } + } + + /// + /// Represents the element at the 4th row and 2nd column of this . + /// + public float Matrix42 + { + get { return _matrix42; } + set { _matrix42 = value; } + } + + /// + /// Represents the element at the 4th row and 3rd column of this . + /// + public float Matrix43 + { + get { return _matrix43; } + set { _matrix43 = value; } + } + + /// + /// Represents the element at the 4th row and 4th column of this . + /// + public float Matrix44 + { + get { return _matrix44; } + set { _matrix44 = value; } + } + + + /// + /// Initializes a new instance of the class with the elements in the specified matrix. + /// + [CLSCompliant(false)] + public ColorMatrix(float[][] newColorMatrix) + { + SetMatrix(newColorMatrix); + } + + internal void SetMatrix(float[][] newColorMatrix) + { + _matrix00 = newColorMatrix[0][0]; + _matrix01 = newColorMatrix[0][1]; + _matrix02 = newColorMatrix[0][2]; + _matrix03 = newColorMatrix[0][3]; + _matrix04 = newColorMatrix[0][4]; + _matrix10 = newColorMatrix[1][0]; + _matrix11 = newColorMatrix[1][1]; + _matrix12 = newColorMatrix[1][2]; + _matrix13 = newColorMatrix[1][3]; + _matrix14 = newColorMatrix[1][4]; + _matrix20 = newColorMatrix[2][0]; + _matrix21 = newColorMatrix[2][1]; + _matrix22 = newColorMatrix[2][2]; + _matrix23 = newColorMatrix[2][3]; + _matrix24 = newColorMatrix[2][4]; + _matrix30 = newColorMatrix[3][0]; + _matrix31 = newColorMatrix[3][1]; + _matrix32 = newColorMatrix[3][2]; + _matrix33 = newColorMatrix[3][3]; + _matrix34 = newColorMatrix[3][4]; + _matrix40 = newColorMatrix[4][0]; + _matrix41 = newColorMatrix[4][1]; + _matrix42 = newColorMatrix[4][2]; + _matrix43 = newColorMatrix[4][3]; + _matrix44 = newColorMatrix[4][4]; + } + + internal float[][] GetMatrix() + { + float[][] returnMatrix = new float[5][]; + + for (int i = 0; i < 5; i++) + returnMatrix[i] = new float[5]; + + returnMatrix[0][0] = _matrix00; + returnMatrix[0][1] = _matrix01; + returnMatrix[0][2] = _matrix02; + returnMatrix[0][3] = _matrix03; + returnMatrix[0][4] = _matrix04; + returnMatrix[1][0] = _matrix10; + returnMatrix[1][1] = _matrix11; + returnMatrix[1][2] = _matrix12; + returnMatrix[1][3] = _matrix13; + returnMatrix[1][4] = _matrix14; + returnMatrix[2][0] = _matrix20; + returnMatrix[2][1] = _matrix21; + returnMatrix[2][2] = _matrix22; + returnMatrix[2][3] = _matrix23; + returnMatrix[2][4] = _matrix24; + returnMatrix[3][0] = _matrix30; + returnMatrix[3][1] = _matrix31; + returnMatrix[3][2] = _matrix32; + returnMatrix[3][3] = _matrix33; + returnMatrix[3][4] = _matrix34; + returnMatrix[4][0] = _matrix40; + returnMatrix[4][1] = _matrix41; + returnMatrix[4][2] = _matrix42; + returnMatrix[4][3] = _matrix43; + returnMatrix[4][4] = _matrix44; + + return returnMatrix; + } + + /// + /// Gets or sets the value of the specified element of this . + /// + public float this[int row, int column] + { + get + { + return GetMatrix()[row][column]; + } + + set + { + float[][] tempMatrix = GetMatrix(); + + tempMatrix[row][column] = value; + + SetMatrix(tempMatrix); + } + } + + internal ref float GetPinnableReference() => ref _matrix00; + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(ColorMatrix), MarshalMode.ManagedToUnmanagedIn, typeof(PinningMarshaller))] + internal static unsafe class PinningMarshaller + { + public static ref float GetPinnableReference(ColorMatrix managed) => ref (managed is null ? ref Unsafe.NullRef() : ref managed.GetPinnableReference()); + + // All usages in our currently supported scenarios will always go through GetPinnableReference + public static float* ConvertToUnmanaged(ColorMatrix _) => throw new UnreachableException(); + } +#endif + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrixFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrixFlags.cs new file mode 100644 index 00000000000..a0c36410f5d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMatrixFlags.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies available options for color-adjusting. GDI+ can adjust color data only, grayscale data only, or both. + /// + public enum ColorMatrixFlag + { + /// + /// Both colors and grayscale are color-adjusted. + /// + Default = 0, + /// + /// Grascale values are not color-adjusted. + /// + SkipGrays = 1, + /// + /// Only grascale values are color-adjusted. + /// + AltGrays = 2 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMode.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMode.cs new file mode 100644 index 00000000000..57e1dd0d0a0 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorMode.cs @@ -0,0 +1,20 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies two modes for color component values. + /// + public enum ColorMode + { + /// + /// Specifies that integer values supplied are 32-bit values. + /// + Argb32Mode = 0, + /// + /// Specifies that integer values supplied are 64-bit values. + /// + Argb64Mode = 1 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorPalette.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorPalette.cs new file mode 100644 index 00000000000..e6e9367a1e7 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ColorPalette.cs @@ -0,0 +1,97 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Imaging +{ + /// + /// Defines an array of colors that make up a color palette. + /// + public sealed class ColorPalette + { + // We don't provide a public constructor for ColorPalette because if we allow + // arbitrary creation of color palettes you could in theroy not only change the color entries, but the size + // of the palette and that is not valid for an image (meaning you cannot change the palette size for an image). + // ColorPalettes are only valid for "indexed" images like GIFs. + + private int _flags; + private Color[] _entries; + + /// + /// Specifies how to interpret the color information in the array of colors. + /// + public int Flags + { + get + { + return _flags; + } + } + + /// + /// Specifies an array of objects. + /// + public Color[] Entries + { + get + { + return _entries; + } + } + + internal ColorPalette(int count) + { + _entries = new Color[count]; + } + + internal ColorPalette() + { + _entries = new Color[1]; + } + + internal unsafe void ConvertFromMemory(IntPtr memory) + { + // Memory layout is: + // UINT Flags + // UINT Count + // ARGB Entries[size] + + byte* pMemory = (byte*)memory; + + _flags = *(int*)pMemory; + + int size = *(int*)(pMemory + 4); + + _entries = new Color[size]; + + for (int i = 0; i < size; i++) + { + int argb = *(int*)(pMemory + 8 + i * 4); + _entries[i] = Color.FromArgb(argb); + } + } + + internal unsafe IntPtr ConvertToMemory() + { + // Memory layout is: + // UINT Flags + // UINT Count + // ARGB Entries[size] + + int length = _entries.Length; + IntPtr memory = Marshal.AllocHGlobal(checked(4 * (2 + length))); + byte* pMemory = (byte*)memory; + + *(int*)pMemory = _flags; + *(int*)(pMemory + 4) = length; + + for (int i = 0; i < length; i++) + { + *(int*)(pMemory + 4 * (i + 2)) = _entries[i].ToArgb(); + } + + return memory; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusFlags.cs new file mode 100644 index 00000000000..e510ae68184 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusFlags.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /** + * EMF+ Flags + */ + internal enum EmfPlusFlags + { + Display = 0x00000001, + NonDualGdi = 0x00000002 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusRecordType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusRecordType.cs new file mode 100644 index 00000000000..ef4fd400503 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfPlusRecordType.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the methods available in a metafile to read and write graphic commands. + /// + public enum EmfPlusRecordType + { + WmfRecordBase = 0x00010000, + WmfSetBkColor = WmfRecordBase | 0x201, + WmfSetBkMode = WmfRecordBase | 0x102, + WmfSetMapMode = WmfRecordBase | 0x103, + WmfSetROP2 = WmfRecordBase | 0x104, + WmfSetRelAbs = WmfRecordBase | 0x105, + WmfSetPolyFillMode = WmfRecordBase | 0x106, + WmfSetStretchBltMode = WmfRecordBase | 0x107, + WmfSetTextCharExtra = WmfRecordBase | 0x108, + WmfSetTextColor = WmfRecordBase | 0x209, + WmfSetTextJustification = WmfRecordBase | 0x20A, + WmfSetWindowOrg = WmfRecordBase | 0x20B, + WmfSetWindowExt = WmfRecordBase | 0x20C, + WmfSetViewportOrg = WmfRecordBase | 0x20D, + WmfSetViewportExt = WmfRecordBase | 0x20E, + WmfOffsetWindowOrg = WmfRecordBase | 0x20F, + WmfScaleWindowExt = WmfRecordBase | 0x410, + WmfOffsetViewportOrg = WmfRecordBase | 0x211, + WmfScaleViewportExt = WmfRecordBase | 0x412, + WmfLineTo = WmfRecordBase | 0x213, + WmfMoveTo = WmfRecordBase | 0x214, + WmfExcludeClipRect = WmfRecordBase | 0x415, + WmfIntersectClipRect = WmfRecordBase | 0x416, + WmfArc = WmfRecordBase | 0x817, + WmfEllipse = WmfRecordBase | 0x418, + WmfFloodFill = WmfRecordBase | 0x419, + WmfPie = WmfRecordBase | 0x81A, + WmfRectangle = WmfRecordBase | 0x41B, + WmfRoundRect = WmfRecordBase | 0x61C, + WmfPatBlt = WmfRecordBase | 0x61D, + WmfSaveDC = WmfRecordBase | 0x01E, + WmfSetPixel = WmfRecordBase | 0x41F, + WmfOffsetCilpRgn = WmfRecordBase | 0x220, + WmfTextOut = WmfRecordBase | 0x521, + WmfBitBlt = WmfRecordBase | 0x922, + WmfStretchBlt = WmfRecordBase | 0xB23, + WmfPolygon = WmfRecordBase | 0x324, + WmfPolyline = WmfRecordBase | 0x325, + WmfEscape = WmfRecordBase | 0x626, + WmfRestoreDC = WmfRecordBase | 0x127, + WmfFillRegion = WmfRecordBase | 0x228, + WmfFrameRegion = WmfRecordBase | 0x429, + WmfInvertRegion = WmfRecordBase | 0x12A, + WmfPaintRegion = WmfRecordBase | 0x12B, + WmfSelectClipRegion = WmfRecordBase | 0x12C, + WmfSelectObject = WmfRecordBase | 0x12D, + WmfSetTextAlign = WmfRecordBase | 0x12E, + WmfChord = WmfRecordBase | 0x830, + WmfSetMapperFlags = WmfRecordBase | 0x231, + WmfExtTextOut = WmfRecordBase | 0xA32, + WmfSetDibToDev = WmfRecordBase | 0xD33, + WmfSelectPalette = WmfRecordBase | 0x234, + WmfRealizePalette = WmfRecordBase | 0x035, + WmfAnimatePalette = WmfRecordBase | 0x436, + WmfSetPalEntries = WmfRecordBase | 0x037, + WmfPolyPolygon = WmfRecordBase | 0x538, + WmfResizePalette = WmfRecordBase | 0x139, + WmfDibBitBlt = WmfRecordBase | 0x940, + WmfDibStretchBlt = WmfRecordBase | 0xb41, + WmfDibCreatePatternBrush = WmfRecordBase | 0x142, + WmfStretchDib = WmfRecordBase | 0xf43, + WmfExtFloodFill = WmfRecordBase | 0x548, + WmfSetLayout = WmfRecordBase | 0x149, // META_SETLAYOUT + WmfDeleteObject = WmfRecordBase | 0x1f0, + WmfCreatePalette = WmfRecordBase | 0x0f7, + WmfCreatePatternBrush = WmfRecordBase | 0x1f9, + WmfCreatePenIndirect = WmfRecordBase | 0x2fa, + WmfCreateFontIndirect = WmfRecordBase | 0x2fb, + WmfCreateBrushIndirect = WmfRecordBase | 0x2fc, + WmfCreateRegion = WmfRecordBase | 0x6ff, + + // Since we have to enumerate GDI records right along with GDI+ records, + // we list all the GDI records here so that they can be part of the + // same enumeration type which is used in the enumeration callback. + + EmfHeader = 1, + EmfPolyBezier = 2, + EmfPolygon = 3, + EmfPolyline = 4, + EmfPolyBezierTo = 5, + EmfPolyLineTo = 6, + EmfPolyPolyline = 7, + EmfPolyPolygon = 8, + EmfSetWindowExtEx = 9, + EmfSetWindowOrgEx = 10, + EmfSetViewportExtEx = 11, + EmfSetViewportOrgEx = 12, + EmfSetBrushOrgEx = 13, + EmfEof = 14, + EmfSetPixelV = 15, + EmfSetMapperFlags = 16, + EmfSetMapMode = 17, + EmfSetBkMode = 18, + EmfSetPolyFillMode = 19, + EmfSetROP2 = 20, + EmfSetStretchBltMode = 21, + EmfSetTextAlign = 22, + EmfSetColorAdjustment = 23, + EmfSetTextColor = 24, + EmfSetBkColor = 25, + EmfOffsetClipRgn = 26, + EmfMoveToEx = 27, + EmfSetMetaRgn = 28, + EmfExcludeClipRect = 29, + EmfIntersectClipRect = 30, + EmfScaleViewportExtEx = 31, + EmfScaleWindowExtEx = 32, + EmfSaveDC = 33, + EmfRestoreDC = 34, + EmfSetWorldTransform = 35, + EmfModifyWorldTransform = 36, + EmfSelectObject = 37, + EmfCreatePen = 38, + EmfCreateBrushIndirect = 39, + EmfDeleteObject = 40, + EmfAngleArc = 41, + EmfEllipse = 42, + EmfRectangle = 43, + EmfRoundRect = 44, + EmfRoundArc = 45, + EmfChord = 46, + EmfPie = 47, + EmfSelectPalette = 48, + EmfCreatePalette = 49, + EmfSetPaletteEntries = 50, + EmfResizePalette = 51, + EmfRealizePalette = 52, + EmfExtFloodFill = 53, + EmfLineTo = 54, + EmfArcTo = 55, + EmfPolyDraw = 56, + EmfSetArcDirection = 57, + EmfSetMiterLimit = 58, + EmfBeginPath = 59, + EmfEndPath = 60, + EmfCloseFigure = 61, + EmfFillPath = 62, + EmfStrokeAndFillPath = 63, + EmfStrokePath = 64, + EmfFlattenPath = 65, + EmfWidenPath = 66, + EmfSelectClipPath = 67, + EmfAbortPath = 68, + EmfReserved069 = 69, + EmfGdiComment = 70, + EmfFillRgn = 71, + EmfFrameRgn = 72, + EmfInvertRgn = 73, + EmfPaintRgn = 74, + EmfExtSelectClipRgn = 75, + EmfBitBlt = 76, + EmfStretchBlt = 77, + EmfMaskBlt = 78, + EmfPlgBlt = 79, + EmfSetDIBitsToDevice = 80, + EmfStretchDIBits = 81, + EmfExtCreateFontIndirect = 82, + EmfExtTextOutA = 83, + EmfExtTextOutW = 84, + EmfPolyBezier16 = 85, + EmfPolygon16 = 86, + EmfPolyline16 = 87, + EmfPolyBezierTo16 = 88, + EmfPolylineTo16 = 89, + EmfPolyPolyline16 = 90, + EmfPolyPolygon16 = 91, + EmfPolyDraw16 = 92, + EmfCreateMonoBrush = 93, + EmfCreateDibPatternBrushPt = 94, + EmfExtCreatePen = 95, + EmfPolyTextOutA = 96, + EmfPolyTextOutW = 97, + EmfSetIcmMode = 98, // EMR_SETICMMODE, + EmfCreateColorSpace = 99, // EMR_CREATECOLORSPACE, + EmfSetColorSpace = 100, // EMR_SETCOLORSPACE, + EmfDeleteColorSpace = 101, // EMR_DELETECOLORSPACE, + EmfGlsRecord = 102, // EMR_GLSRECORD, + EmfGlsBoundedRecord = 103, // EMR_GLSBOUNDEDRECORD, + EmfPixelFormat = 104, // EMR_PIXELFORMAT, + EmfDrawEscape = 105, // EMR_RESERVED_105, + EmfExtEscape = 106, // EMR_RESERVED_106, + EmfStartDoc = 107, // EMR_RESERVED_107, + EmfSmallTextOut = 108, // EMR_RESERVED_108, + EmfForceUfiMapping = 109, // EMR_RESERVED_109, + EmfNamedEscpae = 110, // EMR_RESERVED_110, + EmfColorCorrectPalette = 111, // EMR_COLORCORRECTPALETTE, + EmfSetIcmProfileA = 112, // EMR_SETICMPROFILEA, + EmfSetIcmProfileW = 113, // EMR_SETICMPROFILEW, + EmfAlphaBlend = 114, // EMR_ALPHABLEND, + EmfSetLayout = 115, // EMR_SETLAYOUT, + EmfTransparentBlt = 116, // EMR_TRANSPARENTBLT, + EmfReserved117 = 117, + EmfGradientFill = 118, // EMR_GRADIENTFILL, + EmfSetLinkedUfis = 119, // EMR_RESERVED_119, + EmfSetTextJustification = 120, // EMR_RESERVED_120, + EmfColorMatchToTargetW = 121, // EMR_COLORMATCHTOTARGETW, + EmfCreateColorSpaceW = 122, // EMR_CREATECOLORSPACEW, + EmfMax = 122, + EmfMin = 1, + + // That is the END of the GDI EMF records. + + // Now we start the list of EMF+ records. We leave quite + // a bit of room here for the addition of any new GDI + // records that may be added later. + + EmfPlusRecordBase = 0x00004000, + Invalid = EmfPlusRecordBase, + Header, + EndOfFile, + + Comment, + + GetDC, // the application grabbed the metafile dc + + MultiFormatStart, + MultiFormatSection, + MultiFormatEnd, + + // For all Persistent Objects + Object, + // Drawing Records + Clear, + FillRects, + DrawRects, + FillPolygon, + DrawLines, + FillEllipse, + DrawEllipse, + FillPie, + DrawPie, + DrawArc, + FillRegion, + FillPath, + DrawPath, + FillClosedCurve, + DrawClosedCurve, + DrawCurve, + DrawBeziers, + DrawImage, + DrawImagePoints, + DrawString, + + // Graphics State Records + SetRenderingOrigin, + SetAntiAliasMode, + SetTextRenderingHint, + SetTextContrast, + SetInterpolationMode, + SetPixelOffsetMode, + SetCompositingMode, + SetCompositingQuality, + Save, + Restore, + BeginContainer, + BeginContainerNoParams, + EndContainer, + SetWorldTransform, + ResetWorldTransform, + MultiplyWorldTransform, + TranslateWorldTransform, + ScaleWorldTransform, + RotateWorldTransform, + SetPageTransform, + ResetClip, + SetClipRect, + SetClipPath, + SetClipRegion, + OffsetClip, + + DrawDriverString, + + Total, + + Max = Total - 1, + Min = Header + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfType.cs new file mode 100644 index 00000000000..d7583a98a48 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EmfType.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the metafile type. + /// + public enum EmfType + { + /// + /// Windows enhanced metafile. Contains GDI commands. Metafiles of this type are referred to as an EMF file. + /// + EmfOnly = MetafileType.Emf, + /// + /// Windows enhanced metafile plus. Contains GDI+ commands. Metafiles of this type are referred to as an EMF+ file. + /// + EmfPlusOnly = MetafileType.EmfPlusOnly, + /// + /// Dual Windows enhanced metafile. Contains equivalent GDI and GDI+ commands. Metafiles of this type are referred to as an EMF+ file. + /// + EmfPlusDual = MetafileType.EmfPlusDual + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/Encoder.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/Encoder.cs new file mode 100644 index 00000000000..13d0adf5d7b --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/Encoder.cs @@ -0,0 +1,46 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + public sealed class Encoder + { + public static readonly Encoder Compression = new Encoder(new Guid(unchecked((int)0xe09d739d), unchecked((short)0xccd4), unchecked((short)0x44ee), new byte[] { 0x8e, 0xba, 0x3f, 0xbf, 0x8b, 0xe4, 0xfc, 0x58 })); + public static readonly Encoder ColorDepth = new Encoder(new Guid(0x66087055, unchecked((short)0xad66), unchecked((short)0x4c7c), new byte[] { 0x9a, 0x18, 0x38, 0xa2, 0x31, 0x0b, 0x83, 0x37 })); + public static readonly Encoder ScanMethod = new Encoder(new Guid(0x3a4e2661, (short)0x3109, (short)0x4e56, new byte[] { 0x85, 0x36, 0x42, 0xc1, 0x56, 0xe7, 0xdc, 0xfa })); + public static readonly Encoder Version = new Encoder(new Guid(0x24d18c76, unchecked((short)0x814a), unchecked((short)0x41a4), new byte[] { 0xbf, 0x53, 0x1c, 0x21, 0x9c, 0xcc, 0xf7, 0x97 })); + public static readonly Encoder RenderMethod = new Encoder(new Guid(0x6d42c53a, (short)0x229a, (short)0x4825, new byte[] { 0x8b, 0xb7, 0x5c, 0x99, 0xe2, 0xb9, 0xa8, 0xb8 })); + public static readonly Encoder Quality = new Encoder(new Guid(0x1d5be4b5, unchecked((short)0xfa4a), unchecked((short)0x452d), new byte[] { 0x9c, 0xdd, 0x5d, 0xb3, 0x51, 0x05, 0xe7, 0xeb })); + public static readonly Encoder Transformation = new Encoder(new Guid(unchecked((int)0x8d0eb2d1), unchecked((short)0xa58e), unchecked((short)0x4ea8), new byte[] { 0xaa, 0x14, 0x10, 0x80, 0x74, 0xb7, 0xb6, 0xf9 })); + public static readonly Encoder LuminanceTable = new Encoder(new Guid(unchecked((int)0xedb33bce), unchecked((short)0x0266), unchecked((short)0x4a77), new byte[] { 0xb9, 0x04, 0x27, 0x21, 0x60, 0x99, 0xe7, 0x17 })); + public static readonly Encoder ChrominanceTable = new Encoder(new Guid(unchecked((int)0xf2e455dc), unchecked((short)0x09b3), unchecked((short)0x4316), new byte[] { 0x82, 0x60, 0x67, 0x6a, 0xda, 0x32, 0x48, 0x1c })); + public static readonly Encoder SaveFlag = new Encoder(new Guid(unchecked((int)0x292266fc), unchecked((short)0xac40), unchecked((short)0x47bf), new byte[] { 0x8c, 0xfc, 0xa8, 0x5b, 0x89, 0xa6, 0x55, 0xde })); + + /// + /// An object that is initialized with the globally unique identifier for the color space category. + /// + public static readonly Encoder ColorSpace = new Encoder(new Guid(unchecked((int)0xae7a62a0), unchecked((short)0xee2c), unchecked((short)0x49d8), new byte[] { 0x9d, 0x07, 0x1b, 0xa8, 0xa9, 0x27, 0x59, 0x6e })); + + /// + /// An object that is initialized with the globally unique identifier for the image items category. + /// + public static readonly Encoder ImageItems = new Encoder(new Guid(unchecked((int)0x63875e13), unchecked((short)0x1f1d), unchecked((short)0x45ab), new byte[] { 0x91, 0x95, 0xa2, 0x9b, 0x60, 0x66, 0xa6, 0x50 })); + + /// + /// An object that is initialized with the globally unique identifier for the save as CMYK category. + /// + public static readonly Encoder SaveAsCmyk = new Encoder(new Guid(unchecked((int)0xa219bbc9), unchecked((short)0x0a9d), unchecked((short)0x4005), new byte[] { 0xa3, 0xee, 0x3a, 0x42, 0x1b, 0x8b, 0xb0, 0x6c })); + + private Guid _guid; + + public Encoder(Guid guid) + { + _guid = guid; + } + + public Guid Guid + { + get { return _guid; } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameter.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameter.cs new file mode 100644 index 00000000000..910de3f82e2 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameter.cs @@ -0,0 +1,393 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + [StructLayout(LayoutKind.Sequential)] + public sealed unsafe class EncoderParameter : IDisposable + { + private Guid _parameterGuid; // GUID of the parameter + private readonly int _numberOfValues; // Number of the parameter values + private readonly EncoderParameterValueType _parameterValueType; // Value type, like ValueTypeLONG etc. + private IntPtr _parameterValue; // A pointer to the parameter values + + ~EncoderParameter() + { + DisposeInternal(); + } + + /// + /// Gets/Sets the Encoder for the EncoderPameter. + /// + public Encoder Encoder + { + get + { + return new Encoder(_parameterGuid); + } + set + { + _parameterGuid = value.Guid; + } + } + + /// + /// Gets the EncoderParameterValueType object from the EncoderParameter. + /// + public EncoderParameterValueType Type + { + get + { + return _parameterValueType; + } + } + + /// + /// Gets the EncoderParameterValueType object from the EncoderParameter. + /// + public EncoderParameterValueType ValueType + { + get + { + return _parameterValueType; + } + } + + /// + /// Gets the NumberOfValues from the EncoderParameter. + /// + public int NumberOfValues + { + get + { + return _numberOfValues; + } + } + + public void Dispose() + { + DisposeInternal(); + GC.KeepAlive(this); + GC.SuppressFinalize(this); + } + + private void DisposeInternal() + { + if (_parameterValue != IntPtr.Zero) + Marshal.FreeHGlobal(_parameterValue); + _parameterValue = IntPtr.Zero; + } + + public EncoderParameter(Encoder encoder, byte value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeByte; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(sizeof(byte)); + + *(byte*)_parameterValue = value; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, byte value, bool undefined) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = undefined ? EncoderParameterValueType.ValueTypeUndefined : EncoderParameterValueType.ValueTypeByte; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(sizeof(byte)); + + *(byte*)_parameterValue = value; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, short value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeShort; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(sizeof(short)); + + *(short*)_parameterValue = value; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, long value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeLong; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(sizeof(int)); + + *(int*)_parameterValue = unchecked((int)value); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, int numerator, int denominator) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeRational; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(2 * sizeof(int)); + + ((int*)_parameterValue)[0] = numerator; + ((int*)_parameterValue)[1] = denominator; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, long rangebegin, long rangeend) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeLongRange; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(2 * sizeof(int)); + + ((int*)_parameterValue)[0] = unchecked((int)rangebegin); + ((int*)_parameterValue)[1] = unchecked((int)rangeend); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, + int numerator1, int demoninator1, + int numerator2, int demoninator2) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeRationalRange; + _numberOfValues = 1; + _parameterValue = Marshal.AllocHGlobal(4 * sizeof(int)); + + ((int*)_parameterValue)[0] = numerator1; + ((int*)_parameterValue)[1] = demoninator1; + ((int*)_parameterValue)[2] = numerator2; + ((int*)_parameterValue)[3] = demoninator2; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, string value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeAscii; + _numberOfValues = value.Length; + _parameterValue = Marshal.StringToHGlobalAnsi(value); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, byte[] value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeByte; + _numberOfValues = value.Length; + + _parameterValue = Marshal.AllocHGlobal(_numberOfValues); + + Marshal.Copy(value, 0, _parameterValue, _numberOfValues); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, byte[] value, bool undefined) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = undefined ? EncoderParameterValueType.ValueTypeUndefined : EncoderParameterValueType.ValueTypeByte; + + _numberOfValues = value.Length; + _parameterValue = Marshal.AllocHGlobal(_numberOfValues); + + Marshal.Copy(value, 0, _parameterValue, _numberOfValues); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, short[] value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeShort; + _numberOfValues = value.Length; + _parameterValue = Marshal.AllocHGlobal(checked(_numberOfValues * sizeof(short))); + + Marshal.Copy(value, 0, _parameterValue, _numberOfValues); + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, long[] value) + { + _parameterGuid = encoder.Guid; + + _parameterValueType = EncoderParameterValueType.ValueTypeLong; + _numberOfValues = value.Length; + _parameterValue = Marshal.AllocHGlobal(checked(_numberOfValues * sizeof(int))); + + int* dest = (int*)_parameterValue; + fixed (long* source = value) + { + for (int i = 0; i < value.Length; i++) + { + dest[i] = unchecked((int)source[i]); + } + } + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, int[] numerator, int[] denominator) + { + _parameterGuid = encoder.Guid; + + if (numerator.Length != denominator.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + _parameterValueType = EncoderParameterValueType.ValueTypeRational; + _numberOfValues = numerator.Length; + _parameterValue = Marshal.AllocHGlobal(checked(_numberOfValues * 2 * sizeof(int))); + + for (int i = 0; i < _numberOfValues; i++) + { + ((int*)_parameterValue)[i * 2 + 0] = numerator[i]; + ((int*)_parameterValue)[i * 2 + 1] = denominator[i]; + } + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, long[] rangebegin, long[] rangeend) + { + _parameterGuid = encoder.Guid; + + if (rangebegin.Length != rangeend.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + _parameterValueType = EncoderParameterValueType.ValueTypeLongRange; + _numberOfValues = rangebegin.Length; + _parameterValue = Marshal.AllocHGlobal(checked(_numberOfValues * 2 * sizeof(int))); + + for (int i = 0; i < _numberOfValues; i++) + { + ((int*)_parameterValue)[i * 2 + 0] = unchecked((int)rangebegin[i]); + ((int*)_parameterValue)[i * 2 + 1] = unchecked((int)rangeend[i]); + } + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, + int[] numerator1, int[] denominator1, + int[] numerator2, int[] denominator2) + { + _parameterGuid = encoder.Guid; + + if (numerator1.Length != denominator1.Length || + numerator1.Length != denominator2.Length || + denominator1.Length != denominator2.Length) + throw Gdip.StatusException(Gdip.InvalidParameter); + + _parameterValueType = EncoderParameterValueType.ValueTypeRationalRange; + _numberOfValues = numerator1.Length; + _parameterValue = Marshal.AllocHGlobal(checked(_numberOfValues * 4 * sizeof(int))); + + for (int i = 0; i < _numberOfValues; i++) + { + ((int*)_parameterValue)[i * 4 + 0] = numerator1[i]; + ((int*)_parameterValue)[i * 4 + 1] = denominator1[i]; + ((int*)_parameterValue)[i * 4 + 2] = numerator2[i]; + ((int*)_parameterValue)[i * 4 + 3] = denominator2[i]; + } + GC.KeepAlive(this); + } + + [Obsolete("This constructor has been deprecated. Use EncoderParameter(Encoder encoder, int numberValues, EncoderParameterValueType type, IntPtr value) instead.")] + public EncoderParameter(Encoder encoder, int NumberOfValues, int Type, int Value) + { + int size; + + switch ((EncoderParameterValueType)Type) + { + case EncoderParameterValueType.ValueTypeByte: + case EncoderParameterValueType.ValueTypeAscii: + size = 1; + break; + case EncoderParameterValueType.ValueTypeShort: + size = 2; + break; + case EncoderParameterValueType.ValueTypeLong: + size = 4; + break; + case EncoderParameterValueType.ValueTypeRational: + case EncoderParameterValueType.ValueTypeLongRange: + size = 2 * 4; + break; + case EncoderParameterValueType.ValueTypeUndefined: + size = 1; + break; + case EncoderParameterValueType.ValueTypeRationalRange: + size = 2 * 2 * 4; + break; + default: + throw Gdip.StatusException(Gdip.WrongState); + } + + int bytes = checked(size * NumberOfValues); + + _parameterValue = Marshal.AllocHGlobal(bytes); + + new ReadOnlySpan((void*)Value, bytes).CopyTo(new Span((void*)_parameterValue, bytes)); + + _parameterValueType = (EncoderParameterValueType)Type; + _numberOfValues = NumberOfValues; + _parameterGuid = encoder.Guid; + GC.KeepAlive(this); + } + + public EncoderParameter(Encoder encoder, int numberValues, EncoderParameterValueType type, IntPtr value) + { + int size; + + switch (type) + { + case EncoderParameterValueType.ValueTypeByte: + case EncoderParameterValueType.ValueTypeAscii: + size = 1; + break; + case EncoderParameterValueType.ValueTypeShort: + size = 2; + break; + case EncoderParameterValueType.ValueTypeLong: + size = 4; + break; + case EncoderParameterValueType.ValueTypeRational: + case EncoderParameterValueType.ValueTypeLongRange: + size = 2 * 4; + break; + case EncoderParameterValueType.ValueTypeUndefined: + size = 1; + break; + case EncoderParameterValueType.ValueTypeRationalRange: + size = 2 * 2 * 4; + break; + case EncoderParameterValueType.ValueTypePointer: + size = IntPtr.Size; + break; + default: + throw Gdip.StatusException(Gdip.WrongState); + } + + int bytes = checked(size * numberValues); + + _parameterValue = Marshal.AllocHGlobal(bytes); + + new ReadOnlySpan((void*)value, bytes).CopyTo(new Span((void*)_parameterValue, bytes)); + + _parameterValueType = type; + _numberOfValues = numberValues; + _parameterGuid = encoder.Guid; + GC.KeepAlive(this); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterPrivate.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterPrivate.cs new file mode 100644 index 00000000000..994ac2e1e46 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterPrivate.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Imaging +{ + [StructLayout(LayoutKind.Sequential)] + internal struct EncoderParameterPrivate + { + public Guid ParameterGuid; // GUID of the parameter + public int NumberOfValues; // Number of the parameter values + public EncoderParameterValueType ParameterValueType; // Value type, like ValueTypeLONG etc. + public IntPtr ParameterValue; // A pointer to the parameter values + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterValueType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterValueType.cs new file mode 100644 index 00000000000..11a19e93816 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameterValueType.cs @@ -0,0 +1,52 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies a EncoderParameter data type. + /// + public enum EncoderParameterValueType + { + /// + /// The data is an 8-bit unsigned value. + /// + ValueTypeByte = 1, + /// + /// The data is an 8-bit ASCII value. + /// + ValueTypeAscii = 2, // 8-bit byte containing one 7-bit ASCII code. NULL terminated. + /// + /// The data is a 16-bit unsigned value. + /// + ValueTypeShort = 3, + /// + /// The data is a 32-bit unsigned value. + /// + ValueTypeLong = 4, + /// + /// The data is two long integers, specifying the numerator and the denominator of a rational number, respectively. + /// + ValueTypeRational = 5, // Two Longs. The first Long is the numerator, the second Long expresses the denomintor. + + /// + /// Two longs which specify a range of integer values. + /// The first Long specifies the lower end and the second one specifies the higher end. + /// All values are inclusive at both ends. + /// + ValueTypeLongRange = 6, + /// + /// An 8-bit undefined value that can take any value depending on field definition. + /// + ValueTypeUndefined = 7, + /// + /// Two Rationals. The first Rational specifies the lower end and the second specifies the higher end. + /// All values are inclusive at both ends + /// + ValueTypeRationalRange = 8, + /// + /// The parameter is a pointer to a block of custom metadata. + /// + ValueTypePointer = 9, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameters.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameters.cs new file mode 100644 index 00000000000..4e5620aad3d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderParameters.cs @@ -0,0 +1,106 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + public sealed class EncoderParameters : IDisposable + { + private EncoderParameter[] _param; + + public EncoderParameters(int count) + { + _param = new EncoderParameter[count]; + } + + public EncoderParameters() + { + _param = new EncoderParameter[1]; + } + + public EncoderParameter[] Param + { + get + { + return _param; + } + set + { + _param = value; + } + } + + /// + /// Copy the EncoderParameters data into a chunk of memory to be consumed by native GDI+ code. + /// + /// We need to marshal the EncoderParameters info from/to native GDI+ ourselves since the definition of the managed/unmanaged classes + /// are different and the native class is a bit weird. The native EncoderParameters class is defined in GDI+ as follows: + /// + /// class EncoderParameters { + /// UINT Count; // Number of parameters in this structure + /// EncoderParameter Parameter[1]; // Parameter values + /// }; + /// + /// We don't have the 'Count' field since the managed array contains it. In order for this structure to work with more than one + /// EncoderParameter we need to preallocate memory for the extra n-1 elements, something like this: + /// + /// EncoderParameters* pEncoderParameters = (EncoderParameters*) malloc(sizeof(EncoderParameters) + (n-1) * sizeof(EncoderParameter)); + /// + /// Also, in 64-bit platforms, 'Count' is aligned in 8 bytes (4 extra padding bytes) so we use IntPtr instead of Int32 to account for + /// that. + /// + internal unsafe IntPtr ConvertToMemory() + { + int size = sizeof(EncoderParameterPrivate); + + int length = _param.Length; + IntPtr memory = Marshal.AllocHGlobal(length * size + IntPtr.Size); + + Marshal.WriteIntPtr(memory, (nint)length); + + byte* arrayOffset = (byte*)memory + IntPtr.Size; + + for (int i = 0; i < length; i++) + { + Marshal.StructureToPtr(_param[i], (nint)(arrayOffset + (nint)i * size), false); + } + + return memory; + } + + /// + /// Copy the native GDI+ EncoderParameters data from a chunk of memory into a managed EncoderParameters object. + /// See ConvertToMemory for more info. + /// + internal static unsafe EncoderParameters ConvertFromMemory(IntPtr memory) + { + if (memory == IntPtr.Zero) + { + throw Gdip.StatusException(Gdip.InvalidParameter); + } + + int count = *(int*)memory; + EncoderParameterPrivate* parameters = (EncoderParameterPrivate*)((byte*)memory + IntPtr.Size); + EncoderParameters p = new EncoderParameters(count); + for (int i = 0; i < count; i++) + { + ref readonly EncoderParameterPrivate param = ref parameters[i]; + + p._param[i] = new EncoderParameter(new Encoder(param.ParameterGuid), param.NumberOfValues, param.ParameterValueType, param.ParameterValue); + } + + return p; + } + + public void Dispose() + { + foreach (EncoderParameter p in _param) + { + p?.Dispose(); + } + _param = null!; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderValue.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderValue.cs new file mode 100644 index 00000000000..e0a6d14bd34 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/EncoderValue.cs @@ -0,0 +1,108 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// The EncoderValue enum. + /// + public enum EncoderValue + { + /// + /// Specifies the CMYK color space. + /// + ColorTypeCMYK, + /// + /// Specifies the YCCK color space. + /// + ColorTypeYCCK, + /// + /// Specifies the LZW compression method. + /// + CompressionLZW, + /// + /// For a TIFF image, specifies the CCITT3 compression method. + /// + CompressionCCITT3, + /// + /// For a TIFF image, specifies the CCITT4 compression method. + /// + CompressionCCITT4, + /// + /// For a TIFF image, specifies the RLE compression method. + /// + CompressionRle, + /// + /// For a TIFF image, specifies no compression. + /// + CompressionNone, + /// + /// Specifies interlaced mode. + /// + ScanMethodInterlaced, + /// + /// Specifies non-interlaced mode. + /// + ScanMethodNonInterlaced, + /// + /// For a GIF image, specifies version 87. + /// + VersionGif87, + /// + /// For a GIF images, specifies version 89a. + /// + VersionGif89, + /// + /// Specifies progressive mode. + /// + RenderProgressive, + /// + /// Specifies non-progressive mode. + /// + RenderNonProgressive, + /// + /// For a JPEG image, specifies lossless 90-degree clockwise rotation. + /// + TransformRotate90, + /// + /// For a JPEG image, specifies lossless 180-degree rotation. + /// + TransformRotate180, + /// + /// For a JPEG image, specifies lossless 270-degree clockwise rotation. + /// + TransformRotate270, + /// + /// For a JPEG image, specifies a lossless horizontal flip. + /// + TransformFlipHorizontal, + /// + /// For a JPEG image, specifies a lossless vertical flip. + /// + TransformFlipVertical, + /// + /// Specifies multiframe encoding. + /// + MultiFrame, + /// + /// Specifies the last frame of a multi-frame image. + /// + LastFrame, + /// + /// Specifies that the encoder object is to be closed. + /// + Flush, + /// + /// For a GIF image, specifies the time frame dimension. + /// + FrameDimensionTime, + /// + /// Specifies the resolution frame dimension. + /// + FrameDimensionResolution, + /// + /// For a TIFF image, specifies the page frame dimension + /// + FrameDimensionPage + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs new file mode 100644 index 00000000000..44d36d291cb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/FrameDimension.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; + +namespace System.Drawing.Imaging +{ + public sealed class FrameDimension + { + // Frame dimension GUIDs, from sdkinc\imgguids.h + private static readonly FrameDimension s_time = new FrameDimension(new Guid("{6aedbd6d-3fb5-418a-83a6-7f45229dc872}")); + private static readonly FrameDimension s_resolution = new FrameDimension(new Guid("{84236f7b-3bd3-428f-8dab-4ea1439ca315}")); + private static readonly FrameDimension s_page = new FrameDimension(new Guid("{7462dc86-6180-4c7e-8e3f-ee7333a7a483}")); + + private Guid _guid; + + /// + /// Initializes a new instance of the class with the specified GUID. + /// + public FrameDimension(Guid guid) + { + _guid = guid; + } + + /// + /// Specifies a global unique identifier (GUID) that represents this . + /// + public Guid Guid + { + get { return _guid; } + } + + /// + /// The time dimension. + /// + public static FrameDimension Time + { + get { return s_time; } + } + + /// + /// The resolution dimension. + /// + public static FrameDimension Resolution + { + get { return s_resolution; } + } + + /// + /// The page dimension. + /// + public static FrameDimension Page + { + get { return s_page; } + } + /// + /// Returns a value indicating whether the specified object is an equivalent to + /// this . + /// + public override bool Equals([NotNullWhen(true)] object? o) + { + FrameDimension? format = o as FrameDimension; + if (format == null) + return false; + return _guid == format._guid; + } + + public override int GetHashCode() + { + return _guid.GetHashCode(); + } + + /// + /// Converts this to a human-readable string. + /// + public override string ToString() + { + if (this == s_time) return "Time"; + if (this == s_resolution) return "Resolution"; + if (this == s_page) return "Page"; + return $"[FrameDimension: {_guid}]"; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs new file mode 100644 index 00000000000..21853ff7c84 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageAttributes.cs @@ -0,0 +1,541 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Globalization; +using System.IO; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + // sdkinc\GDIplusImageAttributes.h + + // There are 5 possible sets of color adjustments: + // ColorAdjustDefault, + // ColorAdjustBitmap, + // ColorAdjustBrush, + // ColorAdjustPen, + // ColorAdjustText, + + // Bitmaps, Brushes, Pens, and Text will all use any color adjustments + // that have been set into the default ImageAttributes until their own + // color adjustments have been set. So as soon as any "Set" method is + // called for Bitmaps, Brushes, Pens, or Text, then they start from + // scratch with only the color adjustments that have been set for them. + // Calling Reset removes any individual color adjustments for a type + // and makes it revert back to using all the default color adjustments + // (if any). The SetToIdentity method is a way to force a type to + // have no color adjustments at all, regardless of what previous adjustments + // have been set for the defaults or for that type. + + /// + /// Contains information about how image colors are manipulated during rendering. + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class ImageAttributes : ICloneable, IDisposable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + internal IntPtr nativeImageAttributes; + + internal void SetNativeImageAttributes(IntPtr handle) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException(nameof(handle)); + + nativeImageAttributes = handle; + } + + /// + /// Initializes a new instance of the class. + /// + public ImageAttributes() + { + IntPtr newImageAttributes; + + int status = Gdip.GdipCreateImageAttributes(out newImageAttributes); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + SetNativeImageAttributes(newImageAttributes); + } + + internal ImageAttributes(IntPtr newNativeImageAttributes) + { + SetNativeImageAttributes(newNativeImageAttributes); + } + + /// + /// Cleans up Windows resources for this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { +#if FINALIZATION_WATCH + if (!disposing && nativeImageAttributes != IntPtr.Zero) + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); +#endif + if (nativeImageAttributes != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDisposeImageAttributes(new HandleRef(this, nativeImageAttributes)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) + { + if (ClientUtils.IsSecurityOrCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during Dispose: " + ex.ToString()); + } + finally + { + nativeImageAttributes = IntPtr.Zero; + } + } + } + + /// + /// Cleans up Windows resources for this . + /// + ~ImageAttributes() + { + Dispose(false); + } + + /// + /// Creates an exact copy of this . + /// + public object Clone() + { + IntPtr clone; + + int status = Gdip.GdipCloneImageAttributes( + new HandleRef(this, nativeImageAttributes), + out clone); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return new ImageAttributes(clone); + } + + /// + /// Sets the 5 X 5 color adjust matrix to the specified . + /// + public void SetColorMatrix(ColorMatrix newColorMatrix) + { + SetColorMatrix(newColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default); + } + + /// + /// Sets the 5 X 5 color adjust matrix to the specified 'Matrix' with the specified 'ColorMatrixFlags'. + /// + public void SetColorMatrix(ColorMatrix newColorMatrix, ColorMatrixFlag flags) + { + SetColorMatrix(newColorMatrix, flags, ColorAdjustType.Default); + } + + /// + /// Sets the 5 X 5 color adjust matrix to the specified 'Matrix' with the specified 'ColorMatrixFlags'. + /// + public void SetColorMatrix(ColorMatrix newColorMatrix, ColorMatrixFlag mode, ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesColorMatrix( + new HandleRef(this, nativeImageAttributes), + type, + true, + newColorMatrix, + null, + mode); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + /// + /// Clears the color adjust matrix to all zeroes. + /// + public void ClearColorMatrix() + { + ClearColorMatrix(ColorAdjustType.Default); + } + + /// + /// Clears the color adjust matrix. + /// + public void ClearColorMatrix(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesColorMatrix( + new HandleRef(this, nativeImageAttributes), + type, + false, + null, + null, + ColorMatrixFlag.Default); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + /// + /// Sets a color adjust matrix for image colors and a separate gray scale adjust matrix for gray scale values. + /// + public void SetColorMatrices(ColorMatrix newColorMatrix, ColorMatrix? grayMatrix) + { + SetColorMatrices(newColorMatrix, grayMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default); + } + + public void SetColorMatrices(ColorMatrix newColorMatrix, ColorMatrix? grayMatrix, ColorMatrixFlag flags) + { + SetColorMatrices(newColorMatrix, grayMatrix, flags, ColorAdjustType.Default); + } + + public void SetColorMatrices(ColorMatrix newColorMatrix, ColorMatrix? grayMatrix, ColorMatrixFlag mode, + ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesColorMatrix( + new HandleRef(this, nativeImageAttributes), + type, + true, + newColorMatrix, + grayMatrix, + mode); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetThreshold(float threshold) + { + SetThreshold(threshold, ColorAdjustType.Default); + } + + public void SetThreshold(float threshold, ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesThreshold( + new HandleRef(this, nativeImageAttributes), + type, + true, + threshold); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearThreshold() + { + ClearThreshold(ColorAdjustType.Default); + } + + public void ClearThreshold(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesThreshold( + new HandleRef(this, nativeImageAttributes), + type, + false, + 0.0f); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetGamma(float gamma) + { + SetGamma(gamma, ColorAdjustType.Default); + } + + public void SetGamma(float gamma, ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesGamma( + new HandleRef(this, nativeImageAttributes), + type, + true, + gamma); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearGamma() + { + ClearGamma(ColorAdjustType.Default); + } + + public void ClearGamma(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesGamma( + new HandleRef(this, nativeImageAttributes), + type, + false, + 0.0f); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetNoOp() + { + SetNoOp(ColorAdjustType.Default); + } + + public void SetNoOp(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesNoOp( + new HandleRef(this, nativeImageAttributes), + type, + true); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearNoOp() + { + ClearNoOp(ColorAdjustType.Default); + } + + public void ClearNoOp(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesNoOp( + new HandleRef(this, nativeImageAttributes), + type, + false); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetColorKey(Color colorLow, Color colorHigh) + { + SetColorKey(colorLow, colorHigh, ColorAdjustType.Default); + } + + public void SetColorKey(Color colorLow, Color colorHigh, ColorAdjustType type) + { + int lowInt = colorLow.ToArgb(); + int highInt = colorHigh.ToArgb(); + + int status = Gdip.GdipSetImageAttributesColorKeys( + new HandleRef(this, nativeImageAttributes), + type, + true, + lowInt, + highInt); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearColorKey() + { + ClearColorKey(ColorAdjustType.Default); + } + + public void ClearColorKey(ColorAdjustType type) + { + int zero = 0; + int status = Gdip.GdipSetImageAttributesColorKeys( + new HandleRef(this, nativeImageAttributes), + type, + false, + zero, + zero); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetOutputChannel(ColorChannelFlag flags) + { + SetOutputChannel(flags, ColorAdjustType.Default); + } + + public void SetOutputChannel(ColorChannelFlag flags, ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesOutputChannel( + new HandleRef(this, nativeImageAttributes), + type, + true, + flags); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearOutputChannel() + { + ClearOutputChannel(ColorAdjustType.Default); + } + + public void ClearOutputChannel(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesOutputChannel( + new HandleRef(this, nativeImageAttributes), + type, + false, + ColorChannelFlag.ColorChannelLast); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetOutputChannelColorProfile(string colorProfileFilename) + { + SetOutputChannelColorProfile(colorProfileFilename, ColorAdjustType.Default); + } + + public void SetOutputChannelColorProfile(string colorProfileFilename, + ColorAdjustType type) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(colorProfileFilename); + + int status = Gdip.GdipSetImageAttributesOutputChannelColorProfile( + new HandleRef(this, nativeImageAttributes), + type, + true, + colorProfileFilename); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void ClearOutputChannelColorProfile() + { + ClearOutputChannel(ColorAdjustType.Default); + } + + public void ClearOutputChannelColorProfile(ColorAdjustType type) + { + int status = Gdip.GdipSetImageAttributesOutputChannel( + new HandleRef(this, nativeImageAttributes), + type, + false, + ColorChannelFlag.ColorChannelLast); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void SetRemapTable(ColorMap[] map) + { + SetRemapTable(map, ColorAdjustType.Default); + } + + public unsafe void SetRemapTable(ColorMap[] map, ColorAdjustType type) + { + int index; + int mapSize = map.Length; + int size = 4; // Marshal.SizeOf(index.GetType()); + IntPtr memory = Marshal.AllocHGlobal(checked(mapSize * size * 2)); + byte* pMemory = (byte*)memory; + + try + { + for (index = 0; index < mapSize; index++) + { + Marshal.StructureToPtr(map[index].OldColor.ToArgb(), (IntPtr)(pMemory + (nint)index * size * 2), false); + Marshal.StructureToPtr(map[index].NewColor.ToArgb(), (IntPtr)(pMemory + (nint)index * size * 2 + size), false); + } + + Gdip.CheckStatus(Gdip.GdipSetImageAttributesRemapTable( + new HandleRef(this, nativeImageAttributes), + type, + true, + mapSize, + memory)); + } + finally + { + Marshal.FreeHGlobal(memory); + } + } + + public void ClearRemapTable() + { + ClearRemapTable(ColorAdjustType.Default); + } + + public void ClearRemapTable(ColorAdjustType type) + { + Gdip.CheckStatus(Gdip.GdipSetImageAttributesRemapTable( + new HandleRef(this, nativeImageAttributes), + type, + false, + 0, + IntPtr.Zero)); + } + + public void SetBrushRemapTable(ColorMap[] map) + { + SetRemapTable(map, ColorAdjustType.Brush); + } + + public void ClearBrushRemapTable() + { + ClearRemapTable(ColorAdjustType.Brush); + } + + public void SetWrapMode(WrapMode mode) + { + SetWrapMode(mode, default(Color), false); + } + + public void SetWrapMode(WrapMode mode, Color color) + { + SetWrapMode(mode, color, false); + } + + public void SetWrapMode(WrapMode mode, Color color, bool clamp) + { + int status = Gdip.GdipSetImageAttributesWrapMode( + new HandleRef(this, nativeImageAttributes), + unchecked((int)mode), + color.ToArgb(), + clamp); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + public void GetAdjustedPalette(ColorPalette palette, ColorAdjustType type) + { + // does inplace adjustment + IntPtr memory = palette.ConvertToMemory(); + try + { + Gdip.CheckStatus(Gdip.GdipGetImageAttributesAdjustedPalette( + new HandleRef(this, nativeImageAttributes), + memory, + type)); + palette.ConvertFromMemory(memory); + } + finally + { + if (memory != IntPtr.Zero) + { + Marshal.FreeHGlobal(memory); + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecFlags.cs new file mode 100644 index 00000000000..1be87e7e0d2 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecFlags.cs @@ -0,0 +1,19 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + [Flags] + public enum ImageCodecFlags + { + Encoder = 0x00000001, + Decoder = 0x00000002, + SupportBitmap = 0x00000004, + SupportVector = 0x00000008, + SeekableEncode = 0x00000010, + BlockingDecode = 0x00000020, + Builtin = 0x00010000, + System = 0x00020000, + User = 0x00040000 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfo.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfo.cs new file mode 100644 index 00000000000..750fd9229ad --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfo.cs @@ -0,0 +1,209 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + // sdkinc\imaging.h + public sealed class ImageCodecInfo + { + private Guid _clsid; + private Guid _formatID; + private string? _codecName; + private string? _dllName; + private string? _formatDescription; + private string? _filenameExtension; + private string? _mimeType; + private ImageCodecFlags _flags; + private int _version; + private byte[][]? _signaturePatterns; + private byte[][]? _signatureMasks; + + internal ImageCodecInfo() + { + } + + public Guid Clsid + { + get { return _clsid; } + set { _clsid = value; } + } + + public Guid FormatID + { + get { return _formatID; } + set { _formatID = value; } + } + + public string? CodecName + { + get { return _codecName; } + set { _codecName = value; } + } + + public string? DllName + { + get + { + return _dllName; + } + set + { + _dllName = value; + } + } + + public string? FormatDescription + { + get { return _formatDescription; } + set { _formatDescription = value; } + } + + public string? FilenameExtension + { + get { return _filenameExtension; } + set { _filenameExtension = value; } + } + + public string? MimeType + { + get { return _mimeType; } + set { _mimeType = value; } + } + + public ImageCodecFlags Flags + { + get { return _flags; } + set { _flags = value; } + } + + public int Version + { + get { return _version; } + set { _version = value; } + } + + [CLSCompliant(false)] + public byte[][]? SignaturePatterns + { + get { return _signaturePatterns; } + set { _signaturePatterns = value; } + } + + [CLSCompliant(false)] + public byte[][]? SignatureMasks + { + get { return _signatureMasks; } + set { _signatureMasks = value; } + } + + // Encoder/Decoder selection APIs + + public static ImageCodecInfo[] GetImageDecoders() + { + ImageCodecInfo[] imageCodecs; + int numDecoders; + int size; + + int status = Gdip.GdipGetImageDecodersSize(out numDecoders, out size); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + IntPtr memory = Marshal.AllocHGlobal(size); + + try + { + status = Gdip.GdipGetImageDecoders(numDecoders, size, memory); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + imageCodecs = ImageCodecInfo.ConvertFromMemory(memory, numDecoders); + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return imageCodecs; + } + + public static ImageCodecInfo[] GetImageEncoders() + { + ImageCodecInfo[] imageCodecs; + int numEncoders; + int size; + + int status = Gdip.GdipGetImageEncodersSize(out numEncoders, out size); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + IntPtr memory = Marshal.AllocHGlobal(size); + + try + { + status = Gdip.GdipGetImageEncoders(numEncoders, size, memory); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + + imageCodecs = ImageCodecInfo.ConvertFromMemory(memory, numEncoders); + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return imageCodecs; + } + + private static unsafe ImageCodecInfo[] ConvertFromMemory(IntPtr memoryStart, int numCodecs) + { + ImageCodecInfo[] codecs = new ImageCodecInfo[numCodecs]; + + int index; + + for (index = 0; index < numCodecs; index++) + { + ref readonly ImageCodecInfoPrivate codecp = ref ((ImageCodecInfoPrivate*)memoryStart)[index]; + + var codec = new ImageCodecInfo(); + codec.Clsid = codecp.Clsid; + codec.FormatID = codecp.FormatID; + codec.CodecName = Marshal.PtrToStringUni(codecp.CodecName); + codec.DllName = Marshal.PtrToStringUni(codecp.DllName); + codec.FormatDescription = Marshal.PtrToStringUni(codecp.FormatDescription); + codec.FilenameExtension = Marshal.PtrToStringUni(codecp.FilenameExtension); + codec.MimeType = Marshal.PtrToStringUni(codecp.MimeType); + + codec.Flags = (ImageCodecFlags)codecp.Flags; + codec.Version = (int)codecp.Version; + + codec.SignaturePatterns = new byte[codecp.SigCount][]; + codec.SignatureMasks = new byte[codecp.SigCount][]; + + for (int j = 0; j < codecp.SigCount; j++) + { + codec.SignaturePatterns[j] = new ReadOnlySpan((byte*)codecp.SigPattern + j * codecp.SigSize, codecp.SigSize).ToArray(); + codec.SignatureMasks[j] = new ReadOnlySpan((byte*)codecp.SigMask + j * codecp.SigSize, codecp.SigSize).ToArray(); + } + + codecs[index] = codec; + } + + return codecs; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs new file mode 100644 index 00000000000..ce3a0b9bb7f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageCodecInfoPrivate.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Imaging +{ + // sdkinc\imaging.h + [StructLayout(LayoutKind.Sequential, Pack = 8)] + internal struct ImageCodecInfoPrivate + { + public Guid Clsid; + + public Guid FormatID; + + public IntPtr CodecName; + public IntPtr DllName; + public IntPtr FormatDescription; + public IntPtr FilenameExtension; + public IntPtr MimeType; + + public int Flags; + public int Version; + public int SigCount; + public int SigSize; + + public IntPtr SigPattern; + public IntPtr SigMask; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFlags.cs new file mode 100644 index 00000000000..c1155aa4852 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFlags.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the attributes of the pixel data contained in an object. + /// + [Flags] + public enum ImageFlags + { + /// + /// There is no format information. + /// + None = 0, + + // Low-word: shared with SINKFLAG_x + + /// + /// Pixel data is scalable. + /// + Scalable = 0x0001, + /// + /// Pixel data contains alpha information. + /// + HasAlpha = 0x0002, + HasTranslucent = 0x0004, + /// + /// Pixel data is partially scalable, but there are some limitations. + /// + PartiallyScalable = 0x0008, + + // Low-word: color space definition + + /// + /// Pixel data uses an RGB color space. + /// + ColorSpaceRgb = 0x0010, + /// + /// Pixel data uses a CMYK color space. + /// + ColorSpaceCmyk = 0x0020, + /// + /// Pixel data is grayscale. + /// + ColorSpaceGray = 0x0040, + ColorSpaceYcbcr = 0x0080, + ColorSpaceYcck = 0x0100, + + // Low-word: image size info + + HasRealDpi = 0x1000, + HasRealPixelSize = 0x2000, + + // High-word + + ReadOnly = 0x00010000, + Caching = 0x00020000 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs new file mode 100644 index 00000000000..1a37c8b2db5 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageFormat.cs @@ -0,0 +1,205 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the format of the image. + /// + [TypeConverter(typeof(ImageFormatConverter))] + public sealed class ImageFormat + { + // Format IDs + // private static ImageFormat undefined = new ImageFormat(new Guid("{b96b3ca9-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_memoryBMP = new ImageFormat(new Guid("{b96b3caa-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_bmp = new ImageFormat(new Guid("{b96b3cab-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_emf = new ImageFormat(new Guid("{b96b3cac-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_wmf = new ImageFormat(new Guid("{b96b3cad-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_jpeg = new ImageFormat(new Guid("{b96b3cae-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_png = new ImageFormat(new Guid("{b96b3caf-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_gif = new ImageFormat(new Guid("{b96b3cb0-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_tiff = new ImageFormat(new Guid("{b96b3cb1-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_exif = new ImageFormat(new Guid("{b96b3cb2-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_icon = new ImageFormat(new Guid("{b96b3cb5-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_heif = new ImageFormat(new Guid("{b96b3cb6-0728-11d3-9d7b-0000f81ef32e}")); + private static readonly ImageFormat s_webp = new ImageFormat(new Guid("{b96b3cb7-0728-11d3-9d7b-0000f81ef32e}")); + + private Guid _guid; + + /// + /// Initializes a new instance of the class with the specified GUID. + /// + public ImageFormat(Guid guid) + { + _guid = guid; + } + + /// + /// Specifies a global unique identifier (GUID) that represents this . + /// + public Guid Guid + { + get { return _guid; } + } + + /// + /// Specifies a memory bitmap image format. + /// + public static ImageFormat MemoryBmp + { + get { return s_memoryBMP; } + } + + /// + /// Specifies the bitmap image format. + /// + public static ImageFormat Bmp + { + get { return s_bmp; } + } + + /// + /// Specifies the enhanced Windows metafile image format. + /// + public static ImageFormat Emf + { + get { return s_emf; } + } + + /// + /// Specifies the Windows metafile image format. + /// + public static ImageFormat Wmf + { + get { return s_wmf; } + } + + /// + /// Specifies the GIF image format. + /// + public static ImageFormat Gif + { + get { return s_gif; } + } + + /// + /// Specifies the JPEG image format. + /// + public static ImageFormat Jpeg + { + get { return s_jpeg; } + } + + /// + /// Specifies the W3C PNG image format. + /// + public static ImageFormat Png + { + get { return s_png; } + } + + /// + /// Specifies the Tag Image File Format (TIFF) image format. + /// + public static ImageFormat Tiff + { + get { return s_tiff; } + } + + /// + /// Specifies the Exchangeable Image Format (EXIF). + /// + public static ImageFormat Exif + { + get { return s_exif; } + } + + /// + /// Specifies the Windows icon image format. + /// + public static ImageFormat Icon + { + get { return s_icon; } + } + + /// + /// Specifies the High Efficiency Image Format (HEIF). + /// + /// + /// This format is supported since Windows 10 1809. + /// + [SupportedOSPlatform("windows10.0.17763.0")] + public static ImageFormat Heif + { + get { return s_heif; } + } + + /// + /// Specifies the WebP image format. + /// + /// + /// This format is supported since Windows 10 1809. + /// + [SupportedOSPlatform("windows10.0.17763.0")] + public static ImageFormat Webp + { + get { return s_webp; } + } + + /// + /// Returns a value indicating whether the specified object is an equivalent to this + /// . + /// + public override bool Equals([NotNullWhen(true)] object? o) + { + ImageFormat? format = o as ImageFormat; + if (format == null) + return false; + return _guid == format._guid; + } + + /// + /// Returns a hash code. + /// + public override int GetHashCode() + { + return _guid.GetHashCode(); + } + + // Find any random encoder which supports this format + internal ImageCodecInfo? FindEncoder() + { + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); + foreach (ImageCodecInfo codec in codecs) + { + if (codec.FormatID.Equals(_guid)) + return codec; + } + return null; + } + + /// + /// Converts this to a human-readable string. + /// + public override string ToString() + { + if (this.Guid == s_memoryBMP.Guid) return "MemoryBMP"; + if (this.Guid == s_bmp.Guid) return "Bmp"; + if (this.Guid == s_emf.Guid) return "Emf"; + if (this.Guid == s_wmf.Guid) return "Wmf"; + if (this.Guid == s_gif.Guid) return "Gif"; + if (this.Guid == s_jpeg.Guid) return "Jpeg"; + if (this.Guid == s_png.Guid) return "Png"; + if (this.Guid == s_tiff.Guid) return "Tiff"; + if (this.Guid == s_exif.Guid) return "Exif"; + if (this.Guid == s_icon.Guid) return "Icon"; + if (this.Guid == s_heif.Guid) return "Heif"; + if (this.Guid == s_webp.Guid) return "Webp"; + return $"[ImageFormat: {_guid}]"; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageLockMode.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageLockMode.cs new file mode 100644 index 00000000000..588ea623860 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/ImageLockMode.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + // Access modes used when calling IImage::LockBits + /// + /// Indicates the access mode for an . + /// + public enum ImageLockMode + { + /// + /// Specifies the image is read-only. + /// + ReadOnly = 0x0001, + /// + /// Specifies the image is write-only. + /// + WriteOnly = 0x0002, + /// + /// Specifies the image is read-write. + /// + ReadWrite = ReadOnly | WriteOnly, + /// + /// Indicates the image resides in a user input buffer, to which the user controls access. + /// + UserInputBuffer = 0x0004, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetaHeader.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetaHeader.cs new file mode 100644 index 00000000000..f1a501314d9 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetaHeader.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Imaging +{ + [StructLayout(LayoutKind.Sequential, Pack = 2)] + public sealed class MetaHeader + { + // The ENHMETAHEADER structure is defined natively as a union with WmfHeader. + // Extreme care should be taken if changing the layout of the corresponding managaed + // structures to minimize the risk of buffer overruns. The affected managed classes + // are the following: ENHMETAHEADER, MetaHeader, MetafileHeaderWmf, MetafileHeaderEmf. + private WmfMetaHeader _data; + + public MetaHeader() + { + } + + internal MetaHeader(WmfMetaHeader header) + { + _data._type = header._type; + _data._headerSize = header._headerSize; + _data._version = header._version; + _data._size = header._size; + _data._noObjects = header._noObjects; + _data._maxRecord = header._maxRecord; + _data._noParameters = header._noParameters; + } + + /// + /// Represents the type of the associated . + /// + public short Type + { + get { return _data._type; } + set { _data._type = value; } + } + + /// + /// Represents the sizi, in bytes, of the header file. + /// + public short HeaderSize + { + get { return _data._headerSize; } + set { _data._headerSize = value; } + } + + /// + /// Represents the version number of the header format. + /// + public short Version + { + get { return _data._version; } + set { _data._version = value; } + } + + /// + /// Represents the size, in bytes, of the associated . + /// + public int Size + { + get { return _data._size; } + set { _data._size = value; } + } + + public short NoObjects + { + get { return _data._noObjects; } + set { _data._noObjects = value; } + } + + public int MaxRecord + { + get { return _data._maxRecord; } + set { _data._maxRecord = value; } + } + + public short NoParameters + { + get { return _data._noParameters; } + set { _data._noParameters = value; } + } + + internal WmfMetaHeader GetNativeValue() => _data; + } + + [StructLayout(LayoutKind.Sequential, Pack = 2)] + internal struct WmfMetaHeader + { + internal short _type; + internal short _headerSize; + internal short _version; + internal int _size; + internal short _noObjects; + internal int _maxRecord; + internal short _noParameters; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.cs new file mode 100644 index 00000000000..577761e242f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/Metafile.cs @@ -0,0 +1,701 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.ComponentModel; +using System.Drawing.Internal; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + /// + /// Defines a graphic metafile. A metafile contains records that describe a sequence of graphics operations that + /// can be recorded and played back. + /// + [Editor("System.Drawing.Design.MetafileEditor, System.Drawing.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", + "System.Drawing.Design.UITypeEditor, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + [Serializable] + [TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public sealed class Metafile : Image + { + // GDI+ doesn't handle filenames over MAX_PATH very well + private const int MaxPath = 260; + + /// + /// Initializes a new instance of the class from the specified handle and + /// . + /// + public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader, bool deleteWmf) + { + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromWmf(hmetafile, deleteWmf, wmfHeader, out IntPtr metafile)); + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified handle and + /// . + /// + public Metafile(IntPtr henhmetafile, bool deleteEmf) + { + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromEmf(henhmetafile, deleteEmf, out IntPtr metafile)); + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified filename. + /// + public Metafile(string filename) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(filename); + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromFile(filename, out IntPtr metafile)); + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, Rectangle frameRect) : + this(referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class from the specified handle to a device context. + /// + public Metafile(IntPtr referenceHdc, EmfType emfType) : + this(referenceHdc, emfType, null) + { + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, RectangleF frameRect) : + this(referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit) : + this(referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + { + Gdip.CheckStatus(Gdip.GdipRecordMetafile( + referenceHdc, + type, + ref frameRect, + frameUnit, + description, + out IntPtr metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit) : + this(referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc) : + this(fileName, referenceHdc, EmfType.EmfPlusDual, null) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, EmfType type) : + this(fileName, referenceHdc, type, null) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect) : + this(fileName, referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit) : + this(fileName, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(fileName, referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, string? desc) : + this(fileName, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual, desc) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(fileName); + if (fileName.Length > MaxPath) + { + throw new PathTooLongException(); + } + + Gdip.CheckStatus(Gdip.GdipRecordMetafileFileName( + fileName, + referenceHdc, + type, + ref frameRect, + frameUnit, + description, + out IntPtr metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect) : + this(fileName, referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit) : + this(fileName, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(fileName, referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, string? description) : + this(fileName, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual, description) + { + } + + /// + /// Initializes a new instance of the class from the specified data stream. + /// + public Metafile(Stream stream, IntPtr referenceHdc) : + this(stream, referenceHdc, EmfType.EmfPlusDual, null) + { + } + + /// + /// Initializes a new instance of the class from the specified data stream. + /// + public Metafile(Stream stream, IntPtr referenceHdc, EmfType type) : + this(stream, referenceHdc, type, null) + { + } + + /// + /// Initializes a new instance of the class from the specified data stream. + /// + public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect) : + this(stream, referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit) : + this(stream, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(stream, referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class from the specified data stream. + /// + public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect) : + this(stream, referenceHdc, frameRect, MetafileFrameUnit.GdiCompatible) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit) : + this(stream, referenceHdc, frameRect, frameUnit, EmfType.EmfPlusDual) + { + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type) : + this(stream, referenceHdc, frameRect, frameUnit, type, null) + { + } + + /// + /// Initializes a new instance of the class from the specified handle and + /// . + /// + public Metafile(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader) : + this(hmetafile, wmfHeader, false) + { + } + + /// + /// Initializes a new instance of the class from the specified stream. + /// + public unsafe Metafile(Stream stream) + { + ArgumentNullException.ThrowIfNull(stream); + + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipCreateMetafileFromStream(streamWrapper.Ptr, &metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified handle to a device context. + /// + public Metafile(IntPtr referenceHdc, EmfType emfType, string? description) + { + Gdip.CheckStatus(Gdip.GdipRecordMetafile( + referenceHdc, + emfType, + IntPtr.Zero, + MetafileFrameUnit.GdiCompatible, + description, + out IntPtr metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified device context, bounded + /// by the specified rectangle. + /// + public Metafile(IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? desc) + { + IntPtr metafile; + + if (frameRect.IsEmpty) + { + Gdip.CheckStatus(Gdip.GdipRecordMetafile( + referenceHdc, + type, + IntPtr.Zero, + MetafileFrameUnit.GdiCompatible, + desc, + out metafile)); + } + else + { + Gdip.CheckStatus(Gdip.GdipRecordMetafileI( + referenceHdc, + type, + ref frameRect, + frameUnit, + desc, + out metafile)); + } + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, EmfType type, string? description) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(fileName); + + Gdip.CheckStatus(Gdip.GdipRecordMetafileFileName( + fileName, + referenceHdc, + type, + IntPtr.Zero, + MetafileFrameUnit.GdiCompatible, + description, + out IntPtr metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public Metafile(string fileName, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(fileName); + + IntPtr metafile; + + if (frameRect.IsEmpty) + { + Gdip.CheckStatus(Gdip.GdipRecordMetafileFileName( + fileName, + referenceHdc, + type, + IntPtr.Zero, + frameUnit, + description, + out metafile)); + } + else + { + Gdip.CheckStatus(Gdip.GdipRecordMetafileFileNameI( + fileName, + referenceHdc, + type, + ref frameRect, + frameUnit, + description, + out metafile)); + } + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class from the specified data stream. + /// + public unsafe Metafile(Stream stream, IntPtr referenceHdc, EmfType type, string? description) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( + streamWrapper.Ptr, + referenceHdc, + type, + IntPtr.Zero, + MetafileFrameUnit.GdiCompatible, + description, + &metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public unsafe Metafile(Stream stream, IntPtr referenceHdc, RectangleF frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( + streamWrapper.Ptr, + referenceHdc, + type, + &frameRect, + frameUnit, + description, + &metafile)); + + SetNativeImage(metafile); + } + + /// + /// Initializes a new instance of the class with the specified filename. + /// + public unsafe Metafile(Stream stream, IntPtr referenceHdc, Rectangle frameRect, MetafileFrameUnit frameUnit, EmfType type, string? description) + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + + IntPtr metafile = IntPtr.Zero; + if (frameRect.IsEmpty) + { + Gdip.CheckStatus(Gdip.GdipRecordMetafileStream( + streamWrapper.Ptr, + referenceHdc, + type, + IntPtr.Zero, + frameUnit, + description, + &metafile)); + } + else + { + Gdip.CheckStatus(Gdip.GdipRecordMetafileStreamI( + streamWrapper.Ptr, + referenceHdc, + type, + &frameRect, + frameUnit, + description, + &metafile)); + } + + SetNativeImage(metafile); + } + + private Metafile(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + + /// + /// Initializes a new instance of the class from a native metafile handle. + /// + internal Metafile(IntPtr ptr) => SetNativeImage(ptr); + + /// + /// Plays an EMF+ file. + /// + public void PlayRecord(EmfPlusRecordType recordType, int flags, int dataSize, byte[] data) + { + // Used in conjunction with Graphics.EnumerateMetafile to play an EMF+ + // The data must be DWORD aligned if it's an EMF or EMF+. It must be + // WORD aligned if it's a WMF. + + Gdip.CheckStatus(Gdip.GdipPlayMetafileRecord( + new HandleRef(this, nativeImage), + recordType, + flags, + dataSize, + data)); + } + + /// + /// Returns the associated with the specified . + /// + public static MetafileHeader GetMetafileHeader(IntPtr hmetafile, WmfPlaceableFileHeader wmfHeader) + { + MetafileHeader header = new MetafileHeader + { + wmf = new MetafileHeaderWmf() + }; + + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromWmf(hmetafile, wmfHeader, header.wmf)); + return header; + } + + /// + /// Returns the associated with the specified . + /// + public static MetafileHeader GetMetafileHeader(IntPtr henhmetafile) + { + MetafileHeader header = new MetafileHeader + { + emf = new MetafileHeaderEmf() + }; + + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromEmf(henhmetafile, header.emf)); + return header; + } + + /// + /// Returns the associated with the specified . + /// + public static MetafileHeader GetMetafileHeader(string fileName) + { + // Called in order to emulate exception behavior from .NET Framework related to invalid file paths. + Path.GetFullPath(fileName); + + MetafileHeader header = new MetafileHeader(); + + IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf()); + + try + { + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromFile(fileName, memory)); + + int[] type = new int[] { 0 }; + + Marshal.Copy(memory, type, 0, 1); + + MetafileType metafileType = (MetafileType)type[0]; + + if (metafileType == MetafileType.Wmf || + metafileType == MetafileType.WmfPlaceable) + { + // WMF header + header.wmf = Marshal.PtrToStructure(memory)!; + header.emf = null; + } + else + { + // EMF header + header.wmf = null; + header.emf = Marshal.PtrToStructure(memory)!; + } + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return header; + } + + /// + /// Returns the associated with the specified . + /// + public static MetafileHeader GetMetafileHeader(Stream stream) + { + MetafileHeader header; + + IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf()); + + try + { + using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream)); + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromStream(streamWrapper.Ptr, memory)); + + int[] type = new int[] { 0 }; + + Marshal.Copy(memory, type, 0, 1); + + MetafileType metafileType = (MetafileType)type[0]; + + header = new MetafileHeader(); + + if (metafileType == MetafileType.Wmf || + metafileType == MetafileType.WmfPlaceable) + { + // WMF header + header.wmf = Marshal.PtrToStructure(memory)!; + header.emf = null; + } + else + { + // EMF header + header.wmf = null; + header.emf = Marshal.PtrToStructure(memory)!; + } + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return header; + } + + /// + /// Returns the associated with this . + /// + public MetafileHeader GetMetafileHeader() + { + MetafileHeader header; + + IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf()); + + try + { + Gdip.CheckStatus(Gdip.GdipGetMetafileHeaderFromMetafile(new HandleRef(this, nativeImage), memory)); + + int[] type = new int[] { 0 }; + + Marshal.Copy(memory, type, 0, 1); + + MetafileType metafileType = (MetafileType)type[0]; + + header = new MetafileHeader(); + + if (metafileType == MetafileType.Wmf || + metafileType == MetafileType.WmfPlaceable) + { + // WMF header + header.wmf = Marshal.PtrToStructure(memory)!; + header.emf = null; + } + else + { + // EMF header + header.wmf = null; + header.emf = Marshal.PtrToStructure(memory)!; + } + } + finally + { + Marshal.FreeHGlobal(memory); + } + + return header; + } + + /// + /// Returns a Windows handle to an enhanced . + /// + public IntPtr GetHenhmetafile() + { + Gdip.CheckStatus(Gdip.GdipGetHemfFromMetafile(new HandleRef(this, nativeImage), out IntPtr hEmf)); + return hEmf; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileFrameUnit.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileFrameUnit.cs new file mode 100644 index 00000000000..8b725c15636 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileFrameUnit.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the unit of measurement for the rectangle used to size and position a metafile. + /// This is specified during the creation of the . + /// + public enum MetafileFrameUnit + { + /// + /// Specifies a pixel as the unit of measure. + /// + Pixel = GraphicsUnit.Pixel, + /// + /// Specifies a printer's point as the unit of measure. + /// + Point = GraphicsUnit.Point, + /// + /// Specifies an inch as the unit of measure. + /// + Inch = GraphicsUnit.Inch, + /// + /// Specifies 1/300 of an inch as the unit of measure. + /// + Document = GraphicsUnit.Document, + /// + /// Specifies a millimeter as the unit of measure. + /// + Millimeter = GraphicsUnit.Millimeter, + /// + /// Specifies .01 millimeter as the unit of measure. Provided for compatibility with GDI. + /// + GdiCompatible + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeader.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeader.cs new file mode 100644 index 00000000000..b978161bcac --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeader.cs @@ -0,0 +1,242 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Imaging +{ + /// + /// Contains attributes of an associated . + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class MetafileHeader + { + // determine which to use by nullity + internal MetafileHeaderWmf? wmf; + internal MetafileHeaderEmf? emf; + + internal MetafileHeader() + { + } + + /// + /// Gets the type of the associated . + /// + public MetafileType Type + { + get + { + return IsWmf() ? wmf!.type : emf!.type; + } + } + + /// + /// Gets the size, in bytes, of the associated . + /// + public int MetafileSize + { + get + { + return IsWmf() ? wmf!.size : emf!.size; + } + } + + /// + /// Gets the version number of the associated . + /// + public int Version + { + get + { + return IsWmf() ? wmf!.version : emf!.version; + } + } + + /// + /// Gets the horizontal resolution, in dots-per-inch, of the associated . + /// + public float DpiX + { + get + { + return IsWmf() ? wmf!.dpiX : emf!.dpiX; + } + } + + /// + /// Gets the vertical resolution, in dots-per-inch, of the associated . + /// + public float DpiY + { + get + { + return IsWmf() ? wmf!.dpiY : emf!.dpiY; + } + } + + /// + /// Gets a that bounds the associated . + /// + public Rectangle Bounds + { + get + { + return IsWmf() ? + new Rectangle(wmf!.X, wmf.Y, wmf.Width, wmf.Height) : + new Rectangle(emf!.X, emf.Y, emf.Width, emf.Height); + } + } + + /// + /// Returns a value indicating whether the associated is in the Windows metafile format. + /// + public bool IsWmf() + { + if ((wmf == null) && (emf == null)) + throw Gdip.StatusException(Gdip.InvalidParameter); + + if ((wmf != null) && + ((wmf.type == MetafileType.Wmf) || + (wmf.type == MetafileType.WmfPlaceable))) + return true; + else + return false; + } + + /// + /// Returns a value indicating whether the associated is in the Windows Placeable metafile format. + /// + public bool IsWmfPlaceable() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((wmf != null) && (wmf.type == MetafileType.WmfPlaceable)); + } + + /// + /// Returns a value indicating whether the associated is in the Windows enhanced metafile format. + /// + public bool IsEmf() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((emf != null) && (emf.type == MetafileType.Emf)); + } + + /// + /// Returns a value indicating whether the associated is in the Windows enhanced + /// metafile format or the Windows enhanced metafile plus. + /// + public bool IsEmfOrEmfPlus() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((emf != null) && (emf.type >= MetafileType.Emf)); + } + + /// + /// Returns a value indicating whether the associated is in the Windows enhanced + /// metafile plus format. + /// + public bool IsEmfPlus() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((emf != null) && (emf.type >= MetafileType.EmfPlusOnly)); + } + + /// + /// Returns a value indicating whether the associated is in the Dual enhanced metafile + /// format. This format supports both the enhanced and the enhanced plus format. + /// + public bool IsEmfPlusDual() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((emf != null) && (emf.type == MetafileType.EmfPlusDual)); + } + + /// + /// Returns a value indicating whether the associated supports only the Windows + /// enhanced metafile plus format. + /// + public bool IsEmfPlusOnly() + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return ((emf != null) && (emf.type == MetafileType.EmfPlusOnly)); + } + + /// + /// Returns a value indicating whether the associated is device-dependent. + /// + public bool IsDisplay() + { + return IsEmfPlus() && + (((unchecked((int)emf!.emfPlusFlags)) & ((int)EmfPlusFlags.Display)) != 0); + } + + /// + /// Gets the WMF header file for the associated . + /// + public MetaHeader WmfHeader + { + get + { + if (wmf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return wmf.WmfHeader; + } + } + + /// + /// Gets the size, in bytes, of the enhanced metafile plus header file. + /// + public int EmfPlusHeaderSize + { + get + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return IsWmf() ? wmf!.EmfPlusHeaderSize : emf!.EmfPlusHeaderSize; + } + } + + /// + /// Gets the logical horizontal resolution, in dots-per-inch, of the associated . + /// + public int LogicalDpiX + { + get + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return IsWmf() ? wmf!.LogicalDpiX : emf!.LogicalDpiX; + } + } + + /// + /// Gets the logical vertical resolution, in dots-per-inch, of the associated . + /// + public int LogicalDpiY + { + get + { + if (wmf == null && emf == null) + throw Gdip.StatusException(Gdip.InvalidParameter); + + return IsWmf() ? wmf!.LogicalDpiY : emf!.LogicalDpiX; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs new file mode 100644 index 00000000000..fc4be30cd61 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderEmf.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing.Imaging +{ +#if NET7_0_OR_GREATER + [NativeMarshalling(typeof(PinningMarshaller))] +#endif + [StructLayout(LayoutKind.Sequential)] + internal sealed class MetafileHeaderEmf + { + /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. + /// Extreme care should be taken if changing the layout of the corresponding managed + /// structures to minimize the risk of buffer overruns. The affected managed classes + /// are the following: ENHMETAHEADER, MetaHeader, MetafileHeaderWmf, MetafileHeaderEmf. + public MetafileType type = MetafileType.Invalid; + public int size; + public int version; + public EmfPlusFlags emfPlusFlags; + public float dpiX; + public float dpiY; + public int X; + public int Y; + public int Width; + public int Height; + public SafeNativeMethods.ENHMETAHEADER EmfHeader; + public int EmfPlusHeaderSize; + public int LogicalDpiX; + public int LogicalDpiY; + + internal ref byte GetPinnableReference() => ref Unsafe.As(ref type); + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(MetafileHeaderEmf), MarshalMode.ManagedToUnmanagedIn, typeof(PinningMarshaller))] + internal static unsafe class PinningMarshaller + { + public static ref byte GetPinnableReference(MetafileHeaderEmf managed) => ref (managed is null ? ref Unsafe.NullRef() : ref managed.GetPinnableReference()); + + // All usages in our currently supported scenarios will always go through GetPinnableReference + public static byte* ConvertToUnmanaged(MetafileHeaderEmf _) => throw new UnreachableException(); + } +#endif + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs new file mode 100644 index 00000000000..2f5b386e1e0 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileHeaderWmf.cs @@ -0,0 +1,165 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing.Imaging +{ + [StructLayout(LayoutKind.Sequential, Pack = 8)] + internal sealed class MetafileHeaderWmf + { + /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. + /// Extreme care should be taken if changing the layout of the corresponding managed + /// structures to minimize the risk of buffer overruns. The affected managed classes + /// are the following: ENHMETAHEADER, MetaHeader, MetafileHeaderWmf, MetafileHeaderEmf. + public MetafileType type = MetafileType.Invalid; + public int size = Marshal.SizeOf(); + public int version; + public EmfPlusFlags emfPlusFlags; + public float dpiX; + public float dpiY; + public int X; + public int Y; + public int Width; + public int Height; + + //The below datatype, WmfHeader, file is defined natively + //as a union with EmfHeader. Since EmfHeader is a larger + //structure, we need to pad the struct below so that this + //will marshal correctly. +#pragma warning disable CS0618 // Legacy code: We don't care about using obsolete API's. + [MarshalAs(UnmanagedType.Struct)] +#pragma warning restore CS0618 + public MetaHeader WmfHeader = new MetaHeader(); + public int dummy1; + public int dummy2; + public int dummy3; + public int dummy4; + public int dummy5; + public int dummy6; + public int dummy7; + public int dummy8; + public int dummy9; + public int dummy10; + public int dummy11; + public int dummy12; + public int dummy13; + public int dummy14; + public int dummy15; + public int dummy16; + + public int EmfPlusHeaderSize; + public int LogicalDpiX; + public int LogicalDpiY; + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(MetafileHeaderWmf), MarshalMode.ManagedToUnmanagedRef, typeof(InPlaceMarshaller))] + internal static class Marshaller + { + internal unsafe struct InPlaceMarshaller + { + [StructLayout(LayoutKind.Sequential, Pack = 8)] + internal struct Native + { + /// The ENHMETAHEADER structure is defined natively as a union with WmfHeader. + /// Extreme care should be taken if changing the layout of the corresponding managed + /// structures to minimize the risk of buffer overruns. The affected managed classes + /// are the following: ENHMETAHEADER, MetaHeader, MetafileHeaderWmf, MetafileHeaderEmf. + internal MetafileType type; + internal int size; + internal int version; + internal EmfPlusFlags emfPlusFlags; + internal float dpiX; + internal float dpiY; + internal int X; + internal int Y; + internal int Width; + internal int Height; + internal WmfMetaHeader WmfHeader; + internal int dummy1; + internal int dummy2; + internal int dummy3; + internal int dummy4; + internal int dummy5; + internal int dummy6; + internal int dummy7; + internal int dummy8; + internal int dummy9; + internal int dummy10; + internal int dummy11; + internal int dummy12; + internal int dummy13; + internal int dummy14; + internal int dummy15; + internal int dummy16; + internal int EmfPlusHeaderSize; + internal int LogicalDpiX; + internal int LogicalDpiY; + } + + private MetafileHeaderWmf? _managed; + private Native _native; + + public InPlaceMarshaller() + { + _managed = null; + Unsafe.SkipInit(out _native); + } + + public void FromManaged(MetafileHeaderWmf managed) + { + _managed = managed; + _native.type = managed.type; + _native.size = managed.size; + _native.version = managed.version; + _native.emfPlusFlags = managed.emfPlusFlags; + _native.dpiX = managed.dpiX; + _native.dpiY = managed.dpiY; + _native.X = managed.X; + _native.Y = managed.Y; + _native.Width = managed.Width; + _native.Height = managed.Height; + _native.WmfHeader = managed.WmfHeader.GetNativeValue(); + _native.dummy16 = managed.dummy16; + _native.EmfPlusHeaderSize = managed.EmfPlusHeaderSize; + _native.LogicalDpiX = managed.LogicalDpiX; + _native.LogicalDpiY = managed.LogicalDpiY; + } + + public Native ToUnmanaged() => _native; + + public void FromUnmanaged(Native value) => _native = value; + + public MetafileHeaderWmf ToManaged() + { + Debug.Assert(_managed is not null); + _managed.type = _native.type; + _managed.size = _native.size; + _managed.version = _native.version; + _managed.emfPlusFlags = _native.emfPlusFlags; + _managed.dpiX = _native.dpiX; + _managed.dpiY = _native.dpiY; + _managed.X = _native.X; + _managed.Y = _native.Y; + _managed.Width = _native.Width; + _managed.Height = _native.Height; + _managed.WmfHeader = new MetaHeader(_native.WmfHeader); + _managed.dummy16 = _native.dummy16; + _managed.EmfPlusHeaderSize = _native.EmfPlusHeaderSize; + _managed.LogicalDpiX = _native.LogicalDpiX; + _managed.LogicalDpiY = _native.LogicalDpiY; + return _managed; + } + + public void Free() { } + } + } +#endif + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileType.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileType.cs new file mode 100644 index 00000000000..9f657f79d66 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/MetafileType.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the format of a . + /// + public enum MetafileType + { + /// + /// Specifies an invalid type. + /// + Invalid, + /// + /// Specifies a standard Windows metafile. + /// + Wmf, + /// + /// Specifies a Windows Placeable metafile. + /// + WmfPlaceable, + /// + /// Specifies a Windows enhanced metafile. + /// + Emf, + /// + /// Specifies a Windows enhanced metafile plus. + /// + EmfPlusOnly, + /// + /// Specifies both enhanced and enhanced plus commands in the same file. + /// + EmfPlusDual, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/PaletteFlags.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/PaletteFlags.cs new file mode 100644 index 00000000000..3032df9a13f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/PaletteFlags.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /// + /// Specifies the type of color data in the system palette. The data can be color data with alpha, grayscale only, + /// or halftone data. + /// + [Flags] + public enum PaletteFlags + { + /// + /// Specifies alpha data. + /// + HasAlpha = 0x0001, + /// + /// Specifies grayscale data. + /// + GrayScale = 0x0002, + /// + /// Specifies halftone data. + /// + Halftone = 0x0004 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/PixelFormat.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/PixelFormat.cs new file mode 100644 index 00000000000..dd6cce55c7d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/PixelFormat.cs @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + /* + * In-memory pixel data formats: + * bits 0-7 = format index + * bits 8-15 = pixel size (in bits) + * bits 16-23 = flags + * bits 24-31 = reserved + */ + + // If you modify this file, please update Image.GetColorDepth() + + /// + /// Specifies the format of the color data for each pixel in the image. + /// + public enum PixelFormat + { + /// + /// Specifies that pixel data contains color indexed values which means they are an index to colors in the + /// system color table, as opposed to individual color values. + /// + Indexed = 0x00010000, + /// + /// Specifies that pixel data contains GDI colors. + /// + Gdi = 0x00020000, + /// + /// Specifies that pixel data contains alpha values that are not pre-multiplied. + /// + Alpha = 0x00040000, + /// + /// Specifies that pixel format contains pre-multiplied alpha values. + /// + PAlpha = 0x00080000, // What's this? + Extended = 0x00100000, + Canonical = 0x00200000, + /// + /// Specifies that pixel format is undefined. + /// + Undefined = 0, + /// + /// Specifies that pixel format is a don't care. + /// + DontCare = 0, + // makes it into devtools, we can change this. + /// + /// Specifies that pixel format is 1 bit per pixel indexed color. The color table therefore has two colors in it. + /// + Format1bppIndexed = 1 | (1 << 8) | (int)Indexed | (int)Gdi, + /// + /// Specifies that pixel format is 4 bits per pixel indexed color. The color table therefore has 16 colors in it. + /// + Format4bppIndexed = 2 | (4 << 8) | (int)Indexed | (int)Gdi, + /// + /// Specifies that pixel format is 8 bits per pixel indexed color. The color table therefore has 256 colors in it. + /// + Format8bppIndexed = 3 | (8 << 8) | (int)Indexed | (int)Gdi, + Format16bppGrayScale = 4 | (16 << 8) | (int)Extended, + /// + /// Specifies that pixel format is 16 bits per pixel. The color information specifies 65536 shades of gray. + /// + Format16bppRgb555 = 5 | (16 << 8) | (int)Gdi, + /// + /// Specifies that pixel format is 16 bits per pixel. The color information specifies 32768 shades of color of + /// which 5 bits are red, 5 bits are green and 5 bits are blue. + /// + Format16bppRgb565 = 6 | (16 << 8) | (int)Gdi, + /// + /// Specifies that pixel format is 16 bits per pixel. The color information specifies 32768 shades of color of + /// which 5 bits are red, 5 bits are green, 5 bits are blue and 1 bit is alpha. + /// + Format16bppArgb1555 = 7 | (16 << 8) | (int)Alpha | (int)Gdi, + /// + /// Specifies that pixel format is 24 bits per pixel. The color information specifies 16777216 shades of color + /// of which 8 bits are red, 8 bits are green and 8 bits are blue. + /// + Format24bppRgb = 8 | (24 << 8) | (int)Gdi, + /// + /// Specifies that pixel format is 24 bits per pixel. The color information specifies 16777216 shades of color + /// of which 8 bits are red, 8 bits are green and 8 bits are blue. + /// + Format32bppRgb = 9 | (32 << 8) | (int)Gdi, + /// + /// Specifies that pixel format is 32 bits per pixel. The color information specifies 16777216 shades of color + /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are alpha bits. + /// + Format32bppArgb = 10 | (32 << 8) | (int)Alpha | (int)Gdi | (int)Canonical, + /// + /// Specifies that pixel format is 32 bits per pixel. The color information specifies 16777216 shades of color + /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are pre-multiplied alpha bits. + /// + Format32bppPArgb = 11 | (32 << 8) | (int)Alpha | (int)PAlpha | (int)Gdi, + /// + /// Specifies that pixel format is 48 bits per pixel. The color information specifies 16777216 shades of color + /// of which 8 bits are red, 8 bits are green and 8 bits are blue. The 8 additional bits are alpha bits. + /// + Format48bppRgb = 12 | (48 << 8) | (int)Extended, + /// + /// Specifies pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color of + /// which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are alpha bits. + /// + Format64bppArgb = 13 | (64 << 8) | (int)Alpha | (int)Canonical | (int)Extended, + /// + /// Specifies that pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color + /// of which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are pre-multiplied + /// alpha bits. + /// + Format64bppPArgb = 14 | (64 << 8) | (int)Alpha | (int)PAlpha | (int)Extended, + + /// + /// Specifies that pixel format is 64 bits per pixel. The color information specifies 16777216 shades of color + /// of which 16 bits are red, 16 bits are green and 16 bits are blue. The 16 additional bits are alpha bits. + /// + Max = 15, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/PlayRecordCallback.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/PlayRecordCallback.cs new file mode 100644 index 00000000000..3f14fb001fe --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/PlayRecordCallback.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + public delegate void PlayRecordCallback(EmfPlusRecordType recordType, int flags, int dataSize, IntPtr recordData); +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs new file mode 100644 index 00000000000..cc7974280e3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/PropertyItem.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Imaging +{ + // sdkinc\imaging.h + /// + /// Encapsulates a metadata property to be included in an image file. + /// + public sealed class PropertyItem + { + internal PropertyItem() + { + } + + /// + /// Represents the ID of the property. + /// + public int Id { get; set; } + + /// + /// Represents the length of the property. + /// + public int Len { get; set; } + + /// + /// Represents the type of the property. + /// + public short Type { get; set; } + + /// + /// Contains the property value. + /// + public byte[]? Value { get; set; } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs b/src/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs new file mode 100644 index 00000000000..a6761674c0e --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Imaging/WmfPlaceableFileHeader.cs @@ -0,0 +1,123 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET7_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif + +namespace System.Drawing.Imaging +{ + /// + /// Defines an Placeable Metafile. + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class WmfPlaceableFileHeader + { + private int _key = unchecked((int)0x9aC6CDD7); + private short _hmf; + private short _bboxLeft; + private short _bboxTop; + private short _bboxRight; + private short _bboxBottom; + private short _inch; + private int _reserved; + private short _checksum; + + /// + /// Indicates the presence of a placeable metafile header. + /// + public int Key + { + get { return _key; } + set { _key = value; } + } + + /// + /// Stores the handle of the metafile in memory. + /// + public short Hmf + { + get { return _hmf; } + set { _hmf = value; } + } + + /// + /// The x-coordinate of the upper-left corner of the bounding rectangle of the metafile image on the output device. + /// + public short BboxLeft + { + get { return _bboxLeft; } + set { _bboxLeft = value; } + } + + /// + /// The y-coordinate of the upper-left corner of the bounding rectangle of the metafile image on the output device. + /// + public short BboxTop + { + get { return _bboxTop; } + set { _bboxTop = value; } + } + + /// + /// The x-coordinate of the lower-right corner of the bounding rectangle of the metafile image on the output device. + /// + public short BboxRight + { + get { return _bboxRight; } + set { _bboxRight = value; } + } + + /// + /// The y-coordinate of the lower-right corner of the bounding rectangle of the metafile image on the output device. + /// + public short BboxBottom + { + get { return _bboxBottom; } + set { _bboxBottom = value; } + } + + /// + /// Indicates the number of twips per inch. + /// + public short Inch + { + get { return _inch; } + set { _inch = value; } + } + + /// + /// Reserved. Do not use. + /// + public int Reserved + { + get { return _reserved; } + set { _reserved = value; } + } + + /// + /// Indicates the checksum value for the previous ten WORDs in the header. + /// + public short Checksum + { + get { return _checksum; } + set { _checksum = value; } + } + + internal ref int GetPinnableReference() => ref _key; + +#if NET7_0_OR_GREATER + [CustomMarshaller(typeof(WmfPlaceableFileHeader), MarshalMode.ManagedToUnmanagedIn, typeof(PinningMarshaller))] + internal static unsafe class PinningMarshaller + { + public static ref int GetPinnableReference(WmfPlaceableFileHeader managed) => ref (managed is null ? ref Unsafe.NullRef() : ref managed.GetPinnableReference()); + + // All usages in our currently supported scenarios will always go through GetPinnableReference + public static int* ConvertToUnmanaged(WmfPlaceableFileHeader _) => throw new UnreachableException(); + } +#endif + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.ComWrappers.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.ComWrappers.cs new file mode 100644 index 00000000000..65079d5698a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.ComWrappers.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Buffers; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + public unsafe Interop.HRESULT Clone(IntPtr* ppstm) + { + if (ppstm == null) + { + return Interop.HRESULT.STG_E_INVALIDPOINTER; + } + + // The cloned object should have the same current "position" + var clone = new GPStream(_dataStream) + { + _virtualPosition = _virtualPosition + }; + + *ppstm = DrawingCom.Instance.GetOrCreateComInterfaceForObject(clone, CreateComInterfaceFlags.None); + + return Interop.HRESULT.S_OK; + } + + public unsafe Interop.HRESULT CopyTo(IntPtr pstm, ulong cb, ulong* pcbRead, ulong* pcbWritten) + { + byte[] buffer = ArrayPool.Shared.Rent(4096); + + ulong remaining = cb; + ulong totalWritten = 0; + ulong totalRead = 0; + + fixed (byte* b = buffer) + { + while (remaining > 0) + { + uint read = remaining < (ulong)buffer.Length ? (uint)remaining : (uint)buffer.Length; + Read(b, read, &read); + remaining -= read; + totalRead += read; + + if (read == 0) + { + break; + } + + uint written; + Interop.HRESULT hr = (Interop.HRESULT)WriteToStream(pstm, b, read, &written); + if (hr != Interop.HRESULT.S_OK) + { + return hr; + } + totalWritten += written; + } + } + + ArrayPool.Shared.Return(buffer); + + if (pcbRead != null) + { + *pcbRead = totalRead; + } + + if (pcbWritten != null) + { + *pcbWritten = totalWritten; + } + + return Interop.HRESULT.S_OK; + } + + private static unsafe int WriteToStream(IntPtr pstm, byte* pv, uint cb, uint* pcbWritten) + { + return ((delegate* unmanaged)(*(*(void***)pstm + 4 /* IStream.Write slot */))) + (pstm, pv, cb, pcbWritten); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs new file mode 100644 index 00000000000..524be173b52 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/GPStream.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; + +namespace System.Drawing.Internal +{ + internal sealed partial class GPStream : Interop.Ole32.IStream + { + private readonly Stream _dataStream; + + // to support seeking ahead of the stream length... + private long _virtualPosition = -1; + + internal GPStream(Stream stream, bool makeSeekable = true) + { + if (makeSeekable && !stream.CanSeek) + { + // Copy to a memory stream so we can seek + MemoryStream memoryStream = new MemoryStream(); + stream.CopyTo(memoryStream); + _dataStream = memoryStream; + } + else + { + _dataStream = stream; + } + } + + private void ActualizeVirtualPosition() + { + if (_virtualPosition == -1) + return; + + if (_virtualPosition > _dataStream.Length) + _dataStream.SetLength(_virtualPosition); + + _dataStream.Position = _virtualPosition; + + _virtualPosition = -1; + } + + public void Commit(uint grfCommitFlags) + { + _dataStream.Flush(); + + // Extend the length of the file if needed. + ActualizeVirtualPosition(); + } + + public unsafe void Read(byte* pv, uint cb, uint* pcbRead) + { + ActualizeVirtualPosition(); + + // Stream Span API isn't available in 2.0 + Span buffer = new Span(pv, checked((int)cb)); + int read = _dataStream.Read(buffer); + + if (pcbRead != null) + *pcbRead = (uint)read; + } + + public void Revert() + { + // We never report ourselves as Transacted, so we can just ignore this. + } + + public unsafe void Seek(long dlibMove, SeekOrigin dwOrigin, ulong* plibNewPosition) + { + long position = _virtualPosition; + if (_virtualPosition == -1) + { + position = _dataStream.Position; + } + + long length = _dataStream.Length; + switch (dwOrigin) + { + case SeekOrigin.Begin: + if (dlibMove <= length) + { + _dataStream.Position = dlibMove; + _virtualPosition = -1; + } + else + { + _virtualPosition = dlibMove; + } + break; + case SeekOrigin.End: + if (dlibMove <= 0) + { + _dataStream.Position = length + dlibMove; + _virtualPosition = -1; + } + else + { + _virtualPosition = length + dlibMove; + } + break; + case SeekOrigin.Current: + if (dlibMove + position <= length) + { + _dataStream.Position = position + dlibMove; + _virtualPosition = -1; + } + else + { + _virtualPosition = dlibMove + position; + } + break; + } + + if (plibNewPosition == null) + return; + + if (_virtualPosition != -1) + { + *plibNewPosition = (ulong)_virtualPosition; + } + else + { + *plibNewPosition = (ulong)_dataStream.Position; + } + } + + public void SetSize(ulong value) + { + _dataStream.SetLength(checked((long)value)); + } + + public unsafe void Stat(Interop.Ole32.STATSTG* pstatstg, Interop.Ole32.STATFLAG grfStatFlag) + { + if (pstatstg == null) + { + throw new ArgumentNullException(nameof(pstatstg)); + } + + *pstatstg = new Interop.Ole32.STATSTG + { + cbSize = (ulong)_dataStream.Length, + type = Interop.Ole32.STGTY.STGTY_STREAM, + + // Default read/write access is STGM_READ, which == 0 + grfMode = _dataStream.CanWrite + ? _dataStream.CanRead + ? Interop.Ole32.STGM.STGM_READWRITE + : Interop.Ole32.STGM.STGM_WRITE + : Interop.Ole32.STGM.Default + }; + + if (grfStatFlag == Interop.Ole32.STATFLAG.STATFLAG_DEFAULT) + { + // Caller wants a name + pstatstg->AllocName(_dataStream is FileStream fs ? fs.Name : _dataStream.ToString()); + } + } + + public unsafe void Write(byte* pv, uint cb, uint* pcbWritten) + { + ActualizeVirtualPosition(); + + var buffer = new ReadOnlySpan(pv, checked((int)cb)); + _dataStream.Write(buffer); + + if (pcbWritten != null) + *pcbWritten = cb; + } + + public Interop.HRESULT LockRegion(ulong libOffset, ulong cb, uint dwLockType) + { + // Documented way to say we don't support locking + return Interop.HRESULT.STG_E_INVALIDFUNCTION; + } + + public Interop.HRESULT UnlockRegion(ulong libOffset, ulong cb, uint dwLockType) + { + // Documented way to say we don't support locking + return Interop.HRESULT.STG_E_INVALIDFUNCTION; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs new file mode 100644 index 00000000000..771a95038d8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/GpPathData.cs @@ -0,0 +1,15 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct GpPathData + { + public int Count; + public PointF* Points; + public byte* Types; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/ISystemEventTracker.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/ISystemEventTracker.cs new file mode 100644 index 00000000000..b82b21f24d8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/ISystemEventTracker.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Internal +{ + internal interface ISystemColorTracker + { + void OnSystemColorChanged(); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs b/src/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs new file mode 100644 index 00000000000..384ca75fa78 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Internal/SystemColorTracker.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Microsoft.Win32; + +namespace System.Drawing.Internal +{ + // Keeps track of objects that need to be notified of system color change events. + // Mostly this means maintaining a list of weak references. + internal static class SystemColorTracker + { + // when I tried the self host, it went over 500 but never over 1000. + private const int INITIAL_SIZE = 200; + // If it gets this big, I seriously miscalculated the performance of this object. + private const int WARNING_SIZE = 100000; + private const float EXPAND_THRESHOLD = 0.75f; + private const int EXPAND_FACTOR = 2; + + private static WeakReference[] list = new WeakReference[INITIAL_SIZE]; + private static int count; + private static bool addedTracker; + private static readonly object lockObject = new object(); + + internal static void Add(ISystemColorTracker obj) + { + lock (lockObject) + { + Debug.Assert(list != null, "List is null"); + Debug.Assert(list.Length > 0, "INITIAL_SIZE was initialized after list"); + + if (list.Length == count) + { + GarbageCollectList(); + } + + if (!addedTracker) + { + addedTracker = true; + SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(OnUserPreferenceChanged); + } + + // Strictly speaking, we should grab a lock on this class. But since the chances + // of a problem are so low, the consequences so minimal (something will get accidentally dropped + // from the list), and the performance of locking so lousy, we'll risk it. + int index = count; + count++; + + // COM+ takes forever to Finalize() weak references, so it pays to reuse them. + if (list[index] == null) + { + list[index] = new WeakReference(obj); + } + else + { + Debug.Assert(list[index].Target == null, $"Trying to reuse a weak reference that isn't broken yet: list[{index}], length = {list.Length}"); + list[index].Target = obj; + } + } + } + + private static void CleanOutBrokenLinks() + { + // Partition the list -- valid references in the low indices, broken references in the high indices. + // This is taken straight out of Sedgewick (p. 118 on quicksort). + + // Basic idea is to find a broken reference on the left side of the list, and swap it with + // a valid reference on the right + int right = list.Length - 1; + int left = 0; + + int length = list.Length; + + // Loop invariant: everything to the left of "left" is a valid reference, + // and anything to the right of "right" is broken. + while (true) + { + while (left < length && list[left].Target != null) + left++; + while (right >= 0 && list[right].Target == null) + right--; + + if (left >= right) + { + count = left; + break; + } + + WeakReference temp = list[left]; + list[left] = list[right]; + list[right] = temp; + + left++; + right--; + } + + Debug.Assert(count >= 0 && count <= list.Length, "count not a legal index into list"); + +#if DEBUG + // Check loop invariant. + + // We'd like to assert that any index < count contains a valid pointer, + // but since garbage collection can happen at any time, it may have been broken + // after we partitioned it. + // + // for (int i = 0; i < count; i++) { + // Debug.Assert(list[i].Target != null, "Null found on the left side of the list"); + // } + + for (int i = count; i < list.Length; i++) + { + Debug.Assert(list[i].Target == null, "Partitioning didn't work"); + } +#endif + } + + private static void GarbageCollectList() + { + CleanOutBrokenLinks(); + + if (count / (float)list.Length > EXPAND_THRESHOLD) + { + WeakReference[] newList = new WeakReference[list.Length * EXPAND_FACTOR]; + list.CopyTo(newList, 0); + list = newList; + + Debug.Assert(list.Length < WARNING_SIZE, "SystemColorTracker is using way more memory than expected."); + } + } + + private static void OnUserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + // Update pens and brushes + if (e.Category == UserPreferenceCategory.Color) + { + for (int i = 0; i < count; i++) + { + Debug.Assert(list[i] != null, "null value in active part of list"); + ((ISystemColorTracker?)list[i].Target)?.OnSystemColorChanged(); + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.cs b/src/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.cs new file mode 100644 index 00000000000..85a6e00fced --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/LocalAppContextSwitches.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; + +namespace System +{ + internal static partial class LocalAppContextSwitches + { + private static int s_dontSupportPngFramesInIcons; + public static bool DontSupportPngFramesInIcons + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(@"Switch.System.Drawing.DontSupportPngFramesInIcons", ref s_dontSupportPngFramesInIcons); + } + } + + private static int s_optimizePrintPreview; + public static bool OptimizePrintPreview + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return GetCachedSwitchValue(@"Switch.System.Drawing.Printing.OptimizePrintPreview", ref s_optimizePrintPreview); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/NativeMethods.cs b/src/System.Drawing.Common/src/System/Drawing/NativeMethods.cs new file mode 100644 index 00000000000..4762777ab19 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/NativeMethods.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + internal static class NativeMethods + { + internal static HandleRef NullHandleRef => new HandleRef(null, IntPtr.Zero); + + public const int MAX_PATH = 260; + internal const int SM_REMOTESESSION = 0x1000; + + internal const int DIB_RGB_COLORS = 0; + internal const int BI_BITFIELDS = 3; + internal const int BI_RGB = 0; + + [StructLayout(LayoutKind.Sequential)] + internal struct BITMAPINFOHEADER + { + public int biSize; + public int biWidth; + public int biHeight; + public short biPlanes; + public short biBitCount; + public int biCompression; + public int biSizeImage; + public int biXPelsPerMeter; + public int biYPelsPerMeter; + public int biClrUsed; + public int biClrImportant; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PALETTEENTRY + { + public byte peRed; + public byte peGreen; + public byte peBlue; + public byte peFlags; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RGBQUAD + { + public byte rgbBlue; + public byte rgbGreen; + public byte rgbRed; + public byte rgbReserved; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/NumericsExtensions.cs b/src/System.Drawing.Common/src/System/Drawing/NumericsExtensions.cs new file mode 100644 index 00000000000..5cb3fa10ddd --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/NumericsExtensions.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; + +namespace System.Drawing +{ + /// + /// Helpers to allow using System.Numerics types like the System.Drawing equivalents. + /// + internal static class NumericsExtensions + { + internal static void Translate(this ref Matrix3x2 matrix, Vector2 offset) + { + // Replicating what Matrix.Translate(float offsetX, float offsetY) does. + matrix.M31 += (offset.X * matrix.M11) + (offset.Y * matrix.M21); + matrix.M32 += (offset.X * matrix.M12) + (offset.Y * matrix.M22); + } + + internal static bool IsEmpty(this Vector2 vector) => vector.X == 0 && vector.Y == 0; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Pen.cs b/src/System.Drawing.Common/src/System/Drawing/Pen.cs new file mode 100644 index 00000000000..bbe88baa8aa --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Pen.cs @@ -0,0 +1,918 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Internal; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Defines an object used to draw lines and curves. + /// + public sealed class Pen : MarshalByRefObject, ICloneable, IDisposable, ISystemColorTracker + { +#if FINALIZATION_WATCH + private string _allocationSite = Graphics.GetAllocationStack(); +#endif + + // Handle to native GDI+ pen object. + private IntPtr _nativePen; + + // GDI+ doesn't understand system colors, so we need to cache the value here. + private Color _color; + private bool _immutable; + + // Tracks whether the dash style has been changed to something else than Solid during the lifetime of this object. + private bool _dashStyleWasOrIsNotSolid; + + /// + /// Creates a Pen from a native GDI+ object. + /// + private Pen(IntPtr nativePen) => SetNativePen(nativePen); + + internal Pen(Color color, bool immutable) : this(color) => _immutable = immutable; + + /// + /// Initializes a new instance of the Pen class with the specified . + /// + public Pen(Color color) : this(color, (float)1.0) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// and . + /// + public Pen(Color color, float width) + { + _color = color; + + IntPtr pen; + int status = Gdip.GdipCreatePen1(color.ToArgb(), + width, + (int)GraphicsUnit.World, + out pen); + Gdip.CheckStatus(status); + + SetNativePen(pen); + + if (_color.IsSystemColor) + { + SystemColorTracker.Add(this); + } + } + + /// + /// Initializes a new instance of the Pen class with the specified . + /// + public Pen(Brush brush) : this(brush, (float)1.0) + { + } + + /// + /// Initializes a new instance of the class with the specified and width. + /// + public Pen(Brush brush, float width) + { + ArgumentNullException.ThrowIfNull(brush); + + IntPtr pen; + int status = Gdip.GdipCreatePen2(new HandleRef(brush, brush.NativeBrush), + width, + (int)GraphicsUnit.World, + out pen); + Gdip.CheckStatus(status); + + SetNativePen(pen); + } + + internal void SetNativePen(IntPtr nativePen) + { + Debug.Assert(nativePen != IntPtr.Zero); + _nativePen = nativePen; + } + + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + internal IntPtr NativePen => _nativePen; + + /// + /// Creates an exact copy of this . + /// + public object Clone() + { + IntPtr clonedPen; + int status = Gdip.GdipClonePen(new HandleRef(this, NativePen), out clonedPen); + Gdip.CheckStatus(status); + + return new Pen(clonedPen); + } + + /// + /// Cleans up Windows resources for this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { +#if FINALIZATION_WATCH + if (!disposing && nativePen != IntPtr.Zero) + { + Debug.WriteLine("**********************\nDisposed through finalization:\n" + _allocationSite); + } +#endif + + if (!disposing) + { + // If we are finalizing, then we will be unreachable soon. Finalize calls dispose to + // release resources, so we must make sure that during finalization we are + // not immutable. + _immutable = false; + } + else if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + if (_nativePen != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeletePen(new HandleRef(this, NativePen)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + } + finally + { + _nativePen = IntPtr.Zero; + } + } + } + + /// + /// Cleans up Windows resources for this . + /// + ~Pen() => Dispose(false); + + /// + /// Gets or sets the width of this . + /// + public float Width + { + get + { + var width = new float[] { 0 }; + int status = Gdip.GdipGetPenWidth(new HandleRef(this, NativePen), width); + Gdip.CheckStatus(status); + + return width[0]; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenWidth(new HandleRef(this, NativePen), value); + Gdip.CheckStatus(status); + } + } + + /// + /// Sets the values that determine the style of cap used to end lines drawn by this . + /// + public void SetLineCap(LineCap startCap, LineCap endCap, DashCap dashCap) + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenLineCap197819(new HandleRef(this, NativePen), + unchecked((int)startCap), unchecked((int)endCap), unchecked((int)dashCap)); + Gdip.CheckStatus(status); + } + + /// + /// Gets or sets the cap style used at the beginning of lines drawn with this . + /// + public LineCap StartCap + { + get + { + int startCap; + int status = Gdip.GdipGetPenStartCap(new HandleRef(this, NativePen), out startCap); + Gdip.CheckStatus(status); + + return (LineCap)startCap; + } + set + { + switch (value) + { + case LineCap.Flat: + case LineCap.Square: + case LineCap.Round: + case LineCap.Triangle: + case LineCap.NoAnchor: + case LineCap.SquareAnchor: + case LineCap.RoundAnchor: + case LineCap.DiamondAnchor: + case LineCap.ArrowAnchor: + case LineCap.AnchorMask: + case LineCap.Custom: + break; + default: + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(LineCap)); + } + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenStartCap(new HandleRef(this, NativePen), unchecked((int)value)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the cap style used at the end of lines drawn with this . + /// + public LineCap EndCap + { + get + { + int endCap; + int status = Gdip.GdipGetPenEndCap(new HandleRef(this, NativePen), out endCap); + Gdip.CheckStatus(status); + + return (LineCap)endCap; + } + set + { + switch (value) + { + case LineCap.Flat: + case LineCap.Square: + case LineCap.Round: + case LineCap.Triangle: + case LineCap.NoAnchor: + case LineCap.SquareAnchor: + case LineCap.RoundAnchor: + case LineCap.DiamondAnchor: + case LineCap.ArrowAnchor: + case LineCap.AnchorMask: + case LineCap.Custom: + break; + default: + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(LineCap)); + } + + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenEndCap(new HandleRef(this, NativePen), unchecked((int)value)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets a custom cap style to use at the beginning of lines drawn with this . + /// + public CustomLineCap CustomStartCap + { + get + { + IntPtr lineCap; + int status = Gdip.GdipGetPenCustomStartCap(new HandleRef(this, NativePen), out lineCap); + Gdip.CheckStatus(status); + + return CustomLineCap.CreateCustomLineCapObject(lineCap); + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenCustomStartCap(new HandleRef(this, NativePen), + new HandleRef(value, (value == null) ? IntPtr.Zero : value.nativeCap)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets a custom cap style to use at the end of lines drawn with this . + /// + public CustomLineCap CustomEndCap + { + get + { + IntPtr lineCap; + int status = Gdip.GdipGetPenCustomEndCap(new HandleRef(this, NativePen), out lineCap); + Gdip.CheckStatus(status); + return CustomLineCap.CreateCustomLineCapObject(lineCap); + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenCustomEndCap( + new HandleRef(this, NativePen), + new HandleRef(value, (value == null) ? IntPtr.Zero : value.nativeCap)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the cap style used at the beginning or end of dashed lines drawn with this . + /// + public DashCap DashCap + { + get + { + int dashCap; + int status = Gdip.GdipGetPenDashCap197819(new HandleRef(this, NativePen), out dashCap); + Gdip.CheckStatus(status); + + return (DashCap)dashCap; + } + set + { + if (value != DashCap.Flat && value != DashCap.Round && value != DashCap.Triangle) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(DashCap)); + } + + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenDashCap197819(new HandleRef(this, NativePen), unchecked((int)value)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the join style for the ends of two overlapping lines drawn with this . + /// + public LineJoin LineJoin + { + get + { + int lineJoin; + int status = Gdip.GdipGetPenLineJoin(new HandleRef(this, NativePen), out lineJoin); + Gdip.CheckStatus(status); + + return (LineJoin)lineJoin; + } + set + { + if (value < LineJoin.Miter || value > LineJoin.MiterClipped) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(LineJoin)); + } + + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenLineJoin(new HandleRef(this, NativePen), unchecked((int)value)); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the limit of the thickness of the join on a mitered corner. + /// + public float MiterLimit + { + get + { + var miterLimit = new float[] { 0 }; + int status = Gdip.GdipGetPenMiterLimit(new HandleRef(this, NativePen), miterLimit); + Gdip.CheckStatus(status); + + return miterLimit[0]; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenMiterLimit(new HandleRef(this, NativePen), value); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the alignment for objects drawn with this . + /// + public PenAlignment Alignment + { + get + { + PenAlignment penMode; + int status = Gdip.GdipGetPenMode(new HandleRef(this, NativePen), out penMode); + Gdip.CheckStatus(status); + + return penMode; + } + set + { + if (value < PenAlignment.Center || value > PenAlignment.Right) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PenAlignment)); + } + + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenMode(new HandleRef(this, NativePen), value); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets the geometrical transform for objects drawn with this . + /// + public Matrix Transform + { + get + { + var matrix = new Matrix(); + int status = Gdip.GdipGetPenTransform(new HandleRef(this, NativePen), new HandleRef(matrix, matrix.NativeMatrix)); + Gdip.CheckStatus(status); + + return matrix; + } + + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + int status = Gdip.GdipSetPenTransform(new HandleRef(this, NativePen), new HandleRef(value, value.NativeMatrix)); + Gdip.CheckStatus(status); + } + } + + /// + /// Resets the geometric transform for this to identity. + /// + public void ResetTransform() + { + int status = Gdip.GdipResetPenTransform(new HandleRef(this, NativePen)); + Gdip.CheckStatus(status); + } + + /// + /// Multiplies the transform matrix for this by the specified . + /// + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + /// + /// Multiplies the transform matrix for this by the specified in the specified order. + /// + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + if (matrix.NativeMatrix == IntPtr.Zero) + { + // Disposed matrices should result in a no-op. + return; + } + + int status = Gdip.GdipMultiplyPenTransform(new HandleRef(this, NativePen), + new HandleRef(matrix, matrix.NativeMatrix), + order); + Gdip.CheckStatus(status); + } + + /// + /// Translates the local geometrical transform by the specified dimensions. This method prepends the translation + /// to the transform. + /// + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + /// + /// Translates the local geometrical transform by the specified dimensions in the specified order. + /// + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + int status = Gdip.GdipTranslatePenTransform(new HandleRef(this, NativePen), + dx, dy, order); + Gdip.CheckStatus(status); + } + + /// + /// Scales the local geometric transform by the specified amounts. This method prepends the scaling matrix to the transform. + /// + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + /// + /// Scales the local geometric transform by the specified amounts in the specified order. + /// + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + int status = Gdip.GdipScalePenTransform(new HandleRef(this, NativePen), + sx, sy, order); + Gdip.CheckStatus(status); + } + + /// + /// Rotates the local geometric transform by the specified amount. This method prepends the rotation to the transform. + /// + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + /// + /// Rotates the local geometric transform by the specified amount in the specified order. + /// + public void RotateTransform(float angle, MatrixOrder order) + { + int status = Gdip.GdipRotatePenTransform(new HandleRef(this, NativePen), + angle, order); + Gdip.CheckStatus(status); + } + + private void InternalSetColor(Color value) + { + int status = Gdip.GdipSetPenColor(new HandleRef(this, NativePen), + _color.ToArgb()); + Gdip.CheckStatus(status); + + _color = value; + } + + /// + /// Gets the style of lines drawn with this . + /// + public PenType PenType + { + get + { + int type; + int status = Gdip.GdipGetPenFillType(new HandleRef(this, NativePen), out type); + Gdip.CheckStatus(status); + + return (PenType)type; + } + } + + /// + /// Gets or sets the color of this . + /// + public Color Color + { + get + { + if (_color == Color.Empty) + { + if (PenType != PenType.SolidColor) + { + throw new ArgumentException(SR.GdiplusInvalidParameter); + } + + int colorARGB; + int status = Gdip.GdipGetPenColor(new HandleRef(this, NativePen), out colorARGB); + Gdip.CheckStatus(status); + + _color = Color.FromArgb(colorARGB); + } + + // GDI+ doesn't understand system colors, so we can't use GdipGetPenColor in the general case. + return _color; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + if (value != _color) + { + Color oldColor = _color; + _color = value; + InternalSetColor(value); + + // NOTE: We never remove pens from the active list, so if someone is + // changing their pen colors a lot, this could be a problem. + if (value.IsSystemColor && !oldColor.IsSystemColor) + { + SystemColorTracker.Add(this); + } + } + } + } + + /// + /// Gets or sets the that determines attributes of this . + /// + public Brush Brush + { + get + { + Brush? brush = null; + + switch (PenType) + { + case PenType.SolidColor: + brush = new SolidBrush(GetNativeBrush()); + break; + + case PenType.HatchFill: + brush = new HatchBrush(GetNativeBrush()); + break; + + case PenType.TextureFill: + brush = new TextureBrush(GetNativeBrush()); + break; + + case PenType.PathGradient: + brush = new PathGradientBrush(GetNativeBrush()); + break; + + case PenType.LinearGradient: + brush = new LinearGradientBrush(GetNativeBrush()); + break; + + default: + break; + } + + return brush!; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + int status = Gdip.GdipSetPenBrushFill(new HandleRef(this, NativePen), + new HandleRef(value, value.NativeBrush)); + Gdip.CheckStatus(status); + } + } + + private IntPtr GetNativeBrush() + { + IntPtr nativeBrush; + int status = Gdip.GdipGetPenBrushFill(new HandleRef(this, NativePen), out nativeBrush); + Gdip.CheckStatus(status); + + return nativeBrush; + } + + /// + /// Gets or sets the style used for dashed lines drawn with this . + /// + public DashStyle DashStyle + { + get + { + int dashStyle; + int status = Gdip.GdipGetPenDashStyle(new HandleRef(this, NativePen), out dashStyle); + Gdip.CheckStatus(status); + + return (DashStyle)dashStyle; + } + set + { + if (value < DashStyle.Solid || value > DashStyle.Custom) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(DashStyle)); + } + + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenDashStyle(new HandleRef(this, NativePen), unchecked((int)value)); + Gdip.CheckStatus(status); + + // If we just set the pen style to Custom without defining the custom dash pattern, + // make sure that we can return a valid value. + if (value == DashStyle.Custom) + { + EnsureValidDashPattern(); + } + + if (value != DashStyle.Solid) + { + this._dashStyleWasOrIsNotSolid = true; + } + } + } + + /// + /// This method is called after the user sets the pen's dash style to custom. Here, we make sure that there + /// is a default value set for the custom pattern. + /// + private void EnsureValidDashPattern() + { + int retval; + int status = Gdip.GdipGetPenDashCount(new HandleRef(this, NativePen), out retval); + Gdip.CheckStatus(status); + + if (retval == 0) + { + // Set to a solid pattern. + DashPattern = new float[] { 1 }; + } + } + + /// + /// Gets or sets the distance from the start of a line to the beginning of a dash pattern. + /// + public float DashOffset + { + get + { + var dashOffset = new float[] { 0 }; + int status = Gdip.GdipGetPenDashOffset(new HandleRef(this, NativePen), dashOffset); + Gdip.CheckStatus(status); + + return dashOffset[0]; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + int status = Gdip.GdipSetPenDashOffset(new HandleRef(this, NativePen), value); + Gdip.CheckStatus(status); + } + } + + /// + /// Gets or sets an array of custom dashes and spaces. The dashes are made up of line segments. + /// + public float[] DashPattern + { + get + { + int status = Gdip.GdipGetPenDashCount(new HandleRef(this, NativePen), out int count); + Gdip.CheckStatus(status); + + float[] pattern; + // don't call GdipGetPenDashArray with a 0 count + if (count > 0) + { + pattern = new float[count]; + status = Gdip.GdipGetPenDashArray(new HandleRef(this, NativePen), pattern, count); + Gdip.CheckStatus(status); + } + else if (DashStyle == DashStyle.Solid && !this._dashStyleWasOrIsNotSolid) + { + // Most likely we're replicating an existing System.Drawing bug here, it doesn't make much sense to + // ask for a dash pattern when using a solid dash. + throw new OutOfMemoryException(); + } + else if (DashStyle == DashStyle.Solid) + { + pattern = Array.Empty(); + } + else + { + // special case (not handled inside GDI+) + pattern = new float[1]; + pattern[0] = 1.0f; + } + + return pattern; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + + if (value == null || value.Length == 0) + { + throw new ArgumentException(SR.InvalidDashPattern); + } + + foreach (float val in value) + { + if (val <= 0) + { + throw new ArgumentException(SR.InvalidDashPattern); + } + } + + int count = value.Length; + IntPtr buf = Marshal.AllocHGlobal(checked(4 * count)); + + try + { + Marshal.Copy(value, 0, buf, count); + + int status = Gdip.GdipSetPenDashArray(new HandleRef(this, NativePen), new HandleRef(buf, buf), count); + Gdip.CheckStatus(status); + } + finally + { + Marshal.FreeHGlobal(buf); + } + } + } + + /// + /// Gets or sets an array of custom dashes and spaces. The dashes are made up of line segments. + /// + public float[] CompoundArray + { + get + { + int count; + int status = Gdip.GdipGetPenCompoundCount(new HandleRef(this, NativePen), out count); + Gdip.CheckStatus(status); + + var array = new float[count]; + status = Gdip.GdipGetPenCompoundArray(new HandleRef(this, NativePen), array, count); + Gdip.CheckStatus(status); + + return array; + } + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, nameof(Pen))); + } + + if (value.Length <= 1) + { + throw new ArgumentException(SR.GdiplusInvalidParameter); + } + + foreach (float val in value) + { + if (val < 0 || val > 1) + { + throw new ArgumentException(SR.GdiplusInvalidParameter); + } + } + + int status = Gdip.GdipSetPenCompoundArray(new HandleRef(this, NativePen), value, value.Length); + Gdip.CheckStatus(status); + } + } + + void ISystemColorTracker.OnSystemColorChanged() + { + if (NativePen != IntPtr.Zero) + { + InternalSetColor(_color); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Pens.cs b/src/System.Drawing.Common/src/System/Drawing/Pens.cs new file mode 100644 index 00000000000..33ea6e90de1 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Pens.cs @@ -0,0 +1,324 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public static class Pens + { + private static readonly object s_transparentKey = new object(); + private static readonly object s_aliceBlueKey = new object(); + private static readonly object s_antiqueWhiteKey = new object(); + private static readonly object s_aquaKey = new object(); + private static readonly object s_aquamarineKey = new object(); + private static readonly object s_azureKey = new object(); + private static readonly object s_beigeKey = new object(); + private static readonly object s_bisqueKey = new object(); + private static readonly object s_blackKey = new object(); + private static readonly object s_blanchedAlmondKey = new object(); + private static readonly object s_blueKey = new object(); + private static readonly object s_blueVioletKey = new object(); + private static readonly object s_brownKey = new object(); + private static readonly object s_burlyWoodKey = new object(); + private static readonly object s_cadetBlueKey = new object(); + private static readonly object s_chartreuseKey = new object(); + private static readonly object s_chocolateKey = new object(); + private static readonly object s_coralKey = new object(); + private static readonly object s_cornflowerBlueKey = new object(); + private static readonly object s_cornsilkKey = new object(); + private static readonly object s_crimsonKey = new object(); + private static readonly object s_cyanKey = new object(); + private static readonly object s_darkBlueKey = new object(); + private static readonly object s_darkCyanKey = new object(); + private static readonly object s_darkGoldenrodKey = new object(); + private static readonly object s_darkGrayKey = new object(); + private static readonly object s_darkGreenKey = new object(); + private static readonly object s_darkKhakiKey = new object(); + private static readonly object s_darkMagentaKey = new object(); + private static readonly object s_darkOliveGreenKey = new object(); + private static readonly object s_darkOrangeKey = new object(); + private static readonly object s_darkOrchidKey = new object(); + private static readonly object s_darkRedKey = new object(); + private static readonly object s_darkSalmonKey = new object(); + private static readonly object s_darkSeaGreenKey = new object(); + private static readonly object s_darkSlateBlueKey = new object(); + private static readonly object s_darkSlateGrayKey = new object(); + private static readonly object s_darkTurquoiseKey = new object(); + private static readonly object s_darkVioletKey = new object(); + private static readonly object s_deepPinkKey = new object(); + private static readonly object s_deepSkyBlueKey = new object(); + private static readonly object s_dimGrayKey = new object(); + private static readonly object s_dodgerBlueKey = new object(); + private static readonly object s_firebrickKey = new object(); + private static readonly object s_floralWhiteKey = new object(); + private static readonly object s_forestGreenKey = new object(); + private static readonly object s_fuchsiaKey = new object(); + private static readonly object s_gainsboroKey = new object(); + private static readonly object s_ghostWhiteKey = new object(); + private static readonly object s_goldKey = new object(); + private static readonly object s_goldenrodKey = new object(); + private static readonly object s_grayKey = new object(); + private static readonly object s_greenKey = new object(); + private static readonly object s_greenYellowKey = new object(); + private static readonly object s_honeydewKey = new object(); + private static readonly object s_hotPinkKey = new object(); + private static readonly object s_indianRedKey = new object(); + private static readonly object s_indigoKey = new object(); + private static readonly object s_ivoryKey = new object(); + private static readonly object s_khakiKey = new object(); + private static readonly object s_lavenderKey = new object(); + private static readonly object s_lavenderBlushKey = new object(); + private static readonly object s_lawnGreenKey = new object(); + private static readonly object s_lemonChiffonKey = new object(); + private static readonly object s_lightBlueKey = new object(); + private static readonly object s_lightCoralKey = new object(); + private static readonly object s_lightCyanKey = new object(); + private static readonly object s_lightGoldenrodYellowKey = new object(); + private static readonly object s_lightGreenKey = new object(); + private static readonly object s_lightGrayKey = new object(); + private static readonly object s_lightPinkKey = new object(); + private static readonly object s_lightSalmonKey = new object(); + private static readonly object s_lightSeaGreenKey = new object(); + private static readonly object s_lightSkyBlueKey = new object(); + private static readonly object s_lightSlateGrayKey = new object(); + private static readonly object s_lightSteelBlueKey = new object(); + private static readonly object s_lightYellowKey = new object(); + private static readonly object s_limeKey = new object(); + private static readonly object s_limeGreenKey = new object(); + private static readonly object s_linenKey = new object(); + private static readonly object s_magentaKey = new object(); + private static readonly object s_maroonKey = new object(); + private static readonly object s_mediumAquamarineKey = new object(); + private static readonly object s_mediumBlueKey = new object(); + private static readonly object s_mediumOrchidKey = new object(); + private static readonly object s_mediumPurpleKey = new object(); + private static readonly object s_mediumSeaGreenKey = new object(); + private static readonly object s_mediumSlateBlueKey = new object(); + private static readonly object s_mediumSpringGreenKey = new object(); + private static readonly object s_mediumTurquoiseKey = new object(); + private static readonly object s_mediumVioletRedKey = new object(); + private static readonly object s_midnightBlueKey = new object(); + private static readonly object s_mintCreamKey = new object(); + private static readonly object s_mistyRoseKey = new object(); + private static readonly object s_moccasinKey = new object(); + private static readonly object s_navajoWhiteKey = new object(); + private static readonly object s_navyKey = new object(); + private static readonly object s_oldLaceKey = new object(); + private static readonly object s_oliveKey = new object(); + private static readonly object s_oliveDrabKey = new object(); + private static readonly object s_orangeKey = new object(); + private static readonly object s_orangeRedKey = new object(); + private static readonly object s_orchidKey = new object(); + private static readonly object s_paleGoldenrodKey = new object(); + private static readonly object s_paleGreenKey = new object(); + private static readonly object s_paleTurquoiseKey = new object(); + private static readonly object s_paleVioletRedKey = new object(); + private static readonly object s_papayaWhipKey = new object(); + private static readonly object s_peachPuffKey = new object(); + private static readonly object s_peruKey = new object(); + private static readonly object s_pinkKey = new object(); + private static readonly object s_plumKey = new object(); + private static readonly object s_powderBlueKey = new object(); + private static readonly object s_purpleKey = new object(); + private static readonly object s_redKey = new object(); + private static readonly object s_rosyBrownKey = new object(); + private static readonly object s_royalBlueKey = new object(); + private static readonly object s_saddleBrownKey = new object(); + private static readonly object s_salmonKey = new object(); + private static readonly object s_sandyBrownKey = new object(); + private static readonly object s_seaGreenKey = new object(); + private static readonly object s_seaShellKey = new object(); + private static readonly object s_siennaKey = new object(); + private static readonly object s_silverKey = new object(); + private static readonly object s_skyBlueKey = new object(); + private static readonly object s_slateBlueKey = new object(); + private static readonly object s_slateGrayKey = new object(); + private static readonly object s_snowKey = new object(); + private static readonly object s_springGreenKey = new object(); + private static readonly object s_steelBlueKey = new object(); + private static readonly object s_tanKey = new object(); + private static readonly object s_tealKey = new object(); + private static readonly object s_thistleKey = new object(); + private static readonly object s_tomatoKey = new object(); + private static readonly object s_turquoiseKey = new object(); + private static readonly object s_violetKey = new object(); + private static readonly object s_wheatKey = new object(); + private static readonly object s_whiteKey = new object(); + private static readonly object s_whiteSmokeKey = new object(); + private static readonly object s_yellowKey = new object(); + private static readonly object s_yellowGreenKey = new object(); + + public static Pen Transparent => GetPen(s_transparentKey, Color.Transparent); + + public static Pen AliceBlue => GetPen(s_aliceBlueKey, Color.AliceBlue); + public static Pen AntiqueWhite => GetPen(s_antiqueWhiteKey, Color.AntiqueWhite); + public static Pen Aqua => GetPen(s_aquaKey, Color.Aqua); + public static Pen Aquamarine => GetPen(s_aquamarineKey, Color.Aquamarine); + public static Pen Azure => GetPen(s_azureKey, Color.Azure); + + public static Pen Beige => GetPen(s_beigeKey, Color.Beige); + public static Pen Bisque => GetPen(s_bisqueKey, Color.Bisque); + public static Pen Black => GetPen(s_blackKey, Color.Black); + public static Pen BlanchedAlmond => GetPen(s_blanchedAlmondKey, Color.BlanchedAlmond); + public static Pen Blue => GetPen(s_blueKey, Color.Blue); + public static Pen BlueViolet => GetPen(s_blueVioletKey, Color.BlueViolet); + public static Pen Brown => GetPen(s_brownKey, Color.Brown); + public static Pen BurlyWood => GetPen(s_burlyWoodKey, Color.BurlyWood); + + public static Pen CadetBlue => GetPen(s_cadetBlueKey, Color.CadetBlue); + public static Pen Chartreuse => GetPen(s_chartreuseKey, Color.Chartreuse); + public static Pen Chocolate => GetPen(s_chocolateKey, Color.Chocolate); + public static Pen Coral => GetPen(s_coralKey, Color.Coral); + public static Pen CornflowerBlue => GetPen(s_cornflowerBlueKey, Color.CornflowerBlue); + public static Pen Cornsilk => GetPen(s_cornsilkKey, Color.Cornsilk); + public static Pen Crimson => GetPen(s_crimsonKey, Color.Crimson); + public static Pen Cyan => GetPen(s_cyanKey, Color.Cyan); + + public static Pen DarkBlue => GetPen(s_darkBlueKey, Color.DarkBlue); + public static Pen DarkCyan => GetPen(s_darkCyanKey, Color.DarkCyan); + public static Pen DarkGoldenrod => GetPen(s_darkGoldenrodKey, Color.DarkGoldenrod); + public static Pen DarkGray => GetPen(s_darkGrayKey, Color.DarkGray); + public static Pen DarkGreen => GetPen(s_darkGreenKey, Color.DarkGreen); + public static Pen DarkKhaki => GetPen(s_darkKhakiKey, Color.DarkKhaki); + public static Pen DarkMagenta => GetPen(s_darkMagentaKey, Color.DarkMagenta); + public static Pen DarkOliveGreen => GetPen(s_darkOliveGreenKey, Color.DarkOliveGreen); + public static Pen DarkOrange => GetPen(s_darkOrangeKey, Color.DarkOrange); + public static Pen DarkOrchid => GetPen(s_darkOrchidKey, Color.DarkOrchid); + public static Pen DarkRed => GetPen(s_darkRedKey, Color.DarkRed); + public static Pen DarkSalmon => GetPen(s_darkSalmonKey, Color.DarkSalmon); + public static Pen DarkSeaGreen => GetPen(s_darkSeaGreenKey, Color.DarkSeaGreen); + public static Pen DarkSlateBlue => GetPen(s_darkSlateBlueKey, Color.DarkSlateBlue); + public static Pen DarkSlateGray => GetPen(s_darkSlateGrayKey, Color.DarkSlateGray); + public static Pen DarkTurquoise => GetPen(s_darkTurquoiseKey, Color.DarkTurquoise); + public static Pen DarkViolet => GetPen(s_darkVioletKey, Color.DarkViolet); + public static Pen DeepPink => GetPen(s_deepPinkKey, Color.DeepPink); + public static Pen DeepSkyBlue => GetPen(s_deepSkyBlueKey, Color.DeepSkyBlue); + public static Pen DimGray => GetPen(s_dimGrayKey, Color.DimGray); + public static Pen DodgerBlue => GetPen(s_dodgerBlueKey, Color.DodgerBlue); + + public static Pen Firebrick => GetPen(s_firebrickKey, Color.Firebrick); + public static Pen FloralWhite => GetPen(s_floralWhiteKey, Color.FloralWhite); + public static Pen ForestGreen => GetPen(s_forestGreenKey, Color.ForestGreen); + public static Pen Fuchsia => GetPen(s_fuchsiaKey, Color.Fuchsia); + + public static Pen Gainsboro => GetPen(s_gainsboroKey, Color.Gainsboro); + public static Pen GhostWhite => GetPen(s_ghostWhiteKey, Color.GhostWhite); + public static Pen Gold => GetPen(s_goldKey, Color.Gold); + public static Pen Goldenrod => GetPen(s_goldenrodKey, Color.Goldenrod); + public static Pen Gray => GetPen(s_grayKey, Color.Gray); + public static Pen Green => GetPen(s_greenKey, Color.Green); + public static Pen GreenYellow => GetPen(s_greenYellowKey, Color.GreenYellow); + + public static Pen Honeydew => GetPen(s_honeydewKey, Color.Honeydew); + public static Pen HotPink => GetPen(s_hotPinkKey, Color.HotPink); + + public static Pen IndianRed => GetPen(s_indianRedKey, Color.IndianRed); + public static Pen Indigo => GetPen(s_indigoKey, Color.Indigo); + public static Pen Ivory => GetPen(s_ivoryKey, Color.Ivory); + + public static Pen Khaki => GetPen(s_khakiKey, Color.Khaki); + + public static Pen Lavender => GetPen(s_lavenderKey, Color.Lavender); + public static Pen LavenderBlush => GetPen(s_lavenderBlushKey, Color.LavenderBlush); + public static Pen LawnGreen => GetPen(s_lawnGreenKey, Color.LawnGreen); + public static Pen LemonChiffon => GetPen(s_lemonChiffonKey, Color.LemonChiffon); + public static Pen LightBlue => GetPen(s_lightBlueKey, Color.LightBlue); + public static Pen LightCoral => GetPen(s_lightCoralKey, Color.LightCoral); + public static Pen LightCyan => GetPen(s_lightCyanKey, Color.LightCyan); + public static Pen LightGoldenrodYellow => GetPen(s_lightGoldenrodYellowKey, Color.LightGoldenrodYellow); + public static Pen LightGreen => GetPen(s_lightGreenKey, Color.LightGreen); + public static Pen LightGray => GetPen(s_lightGrayKey, Color.LightGray); + public static Pen LightPink => GetPen(s_lightPinkKey, Color.LightPink); + public static Pen LightSalmon => GetPen(s_lightSalmonKey, Color.LightSalmon); + public static Pen LightSeaGreen => GetPen(s_lightSeaGreenKey, Color.LightSeaGreen); + public static Pen LightSkyBlue => GetPen(s_lightSkyBlueKey, Color.LightSkyBlue); + public static Pen LightSlateGray => GetPen(s_lightSlateGrayKey, Color.LightSlateGray); + public static Pen LightSteelBlue => GetPen(s_lightSteelBlueKey, Color.LightSteelBlue); + public static Pen LightYellow => GetPen(s_lightYellowKey, Color.LightYellow); + public static Pen Lime => GetPen(s_limeKey, Color.Lime); + public static Pen LimeGreen => GetPen(s_limeGreenKey, Color.LimeGreen); + public static Pen Linen => GetPen(s_linenKey, Color.Linen); + + public static Pen Magenta => GetPen(s_magentaKey, Color.Magenta); + public static Pen Maroon => GetPen(s_maroonKey, Color.Maroon); + public static Pen MediumAquamarine => GetPen(s_mediumAquamarineKey, Color.MediumAquamarine); + public static Pen MediumBlue => GetPen(s_mediumBlueKey, Color.MediumBlue); + public static Pen MediumOrchid => GetPen(s_mediumOrchidKey, Color.MediumOrchid); + public static Pen MediumPurple => GetPen(s_mediumPurpleKey, Color.MediumPurple); + public static Pen MediumSeaGreen => GetPen(s_mediumSeaGreenKey, Color.MediumSeaGreen); + public static Pen MediumSlateBlue => GetPen(s_mediumSlateBlueKey, Color.MediumSlateBlue); + public static Pen MediumSpringGreen => GetPen(s_mediumSpringGreenKey, Color.MediumSpringGreen); + public static Pen MediumTurquoise => GetPen(s_mediumTurquoiseKey, Color.MediumTurquoise); + public static Pen MediumVioletRed => GetPen(s_mediumVioletRedKey, Color.MediumVioletRed); + public static Pen MidnightBlue => GetPen(s_midnightBlueKey, Color.MidnightBlue); + public static Pen MintCream => GetPen(s_mintCreamKey, Color.MintCream); + public static Pen MistyRose => GetPen(s_mistyRoseKey, Color.MistyRose); + public static Pen Moccasin => GetPen(s_moccasinKey, Color.Moccasin); + + public static Pen NavajoWhite => GetPen(s_navajoWhiteKey, Color.NavajoWhite); + public static Pen Navy => GetPen(s_navyKey, Color.Navy); + + public static Pen OldLace => GetPen(s_oldLaceKey, Color.OldLace); + public static Pen Olive => GetPen(s_oliveKey, Color.Olive); + public static Pen OliveDrab => GetPen(s_oliveDrabKey, Color.OliveDrab); + public static Pen Orange => GetPen(s_orangeKey, Color.Orange); + public static Pen OrangeRed => GetPen(s_orangeRedKey, Color.OrangeRed); + public static Pen Orchid => GetPen(s_orchidKey, Color.Orchid); + + public static Pen PaleGoldenrod => GetPen(s_paleGoldenrodKey, Color.PaleGoldenrod); + public static Pen PaleGreen => GetPen(s_paleGreenKey, Color.PaleGreen); + public static Pen PaleTurquoise => GetPen(s_paleTurquoiseKey, Color.PaleTurquoise); + public static Pen PaleVioletRed => GetPen(s_paleVioletRedKey, Color.PaleVioletRed); + public static Pen PapayaWhip => GetPen(s_papayaWhipKey, Color.PapayaWhip); + public static Pen PeachPuff => GetPen(s_peachPuffKey, Color.PeachPuff); + public static Pen Peru => GetPen(s_peruKey, Color.Peru); + public static Pen Pink => GetPen(s_pinkKey, Color.Pink); + public static Pen Plum => GetPen(s_plumKey, Color.Plum); + public static Pen PowderBlue => GetPen(s_powderBlueKey, Color.PowderBlue); + public static Pen Purple => GetPen(s_purpleKey, Color.Purple); + + public static Pen Red => GetPen(s_redKey, Color.Red); + public static Pen RosyBrown => GetPen(s_rosyBrownKey, Color.RosyBrown); + public static Pen RoyalBlue => GetPen(s_royalBlueKey, Color.RoyalBlue); + + public static Pen SaddleBrown => GetPen(s_saddleBrownKey, Color.SaddleBrown); + public static Pen Salmon => GetPen(s_salmonKey, Color.Salmon); + public static Pen SandyBrown => GetPen(s_sandyBrownKey, Color.SandyBrown); + public static Pen SeaGreen => GetPen(s_seaGreenKey, Color.SeaGreen); + public static Pen SeaShell => GetPen(s_seaShellKey, Color.SeaShell); + public static Pen Sienna => GetPen(s_siennaKey, Color.Sienna); + public static Pen Silver => GetPen(s_silverKey, Color.Silver); + public static Pen SkyBlue => GetPen(s_skyBlueKey, Color.SkyBlue); + public static Pen SlateBlue => GetPen(s_slateBlueKey, Color.SlateBlue); + public static Pen SlateGray => GetPen(s_slateGrayKey, Color.SlateGray); + public static Pen Snow => GetPen(s_snowKey, Color.Snow); + public static Pen SpringGreen => GetPen(s_springGreenKey, Color.SpringGreen); + public static Pen SteelBlue => GetPen(s_steelBlueKey, Color.SteelBlue); + + public static Pen Tan => GetPen(s_tanKey, Color.Tan); + public static Pen Teal => GetPen(s_tealKey, Color.Teal); + public static Pen Thistle => GetPen(s_thistleKey, Color.Thistle); + public static Pen Tomato => GetPen(s_tomatoKey, Color.Tomato); + public static Pen Turquoise => GetPen(s_turquoiseKey, Color.Turquoise); + + public static Pen Violet => GetPen(s_violetKey, Color.Violet); + + public static Pen Wheat => GetPen(s_wheatKey, Color.Wheat); + public static Pen White => GetPen(s_whiteKey, Color.White); + public static Pen WhiteSmoke => GetPen(s_whiteSmokeKey, Color.WhiteSmoke); + + public static Pen Yellow => GetPen(s_yellowKey, Color.Yellow); + public static Pen YellowGreen => GetPen(s_yellowGreenKey, Color.YellowGreen); + + private static Pen GetPen(object key, Color color) + { + Pen? Pen = (Pen?)Gdip.ThreadData[key]; + if (Pen == null) + { + Pen = new Pen(color, true); + Gdip.ThreadData[key] = Pen; + } + return Pen; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/PointConverter.cs b/src/System.Drawing.Common/src/System/Drawing/PointConverter.cs new file mode 100644 index 00000000000..8079717120c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/PointConverter.cs @@ -0,0 +1,191 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Reflection; + + /// + /// + /// PointConverter is a class that can be used to convert + /// Point from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class PointConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, [NotNullWhen(true)] Type? destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + string strValue = value as string; + + if (strValue != null) { + + string text = strValue.Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 2 integer values. + // + culture ??= CultureInfo.CurrentCulture; + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(sep); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 2) { + return new Point(values[0], values[1]); + } + else { + throw new ArgumentException(SR.Format(SR.TextParseFailedFormat, + text, + "x, y")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the destination type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException(nameof(destinationType)); + } + + if (value is Point){ + if (destinationType == typeof(string)) { + Point pt = (Point)value; + + culture ??= CultureInfo.CurrentCulture; + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args = new string[2]; + int nArg = 0; + + // Note: ConvertToString will raise exception if value cannot be converted. + args[nArg++] = intConverter.ConvertToString(context, culture, pt.X); + args[nArg++] = intConverter.ConvertToString(context, culture, pt.Y); + + return string.Join(sep, args); + } + if (destinationType == typeof(InstanceDescriptor)) { + Point pt = (Point)value; + + ConstructorInfo ctor = typeof(Point).GetConstructor(new Type[] {typeof(int), typeof(int)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {pt.X, pt.Y}); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + if ( propertyValues == null ) { + throw new ArgumentNullException( nameof(propertyValues) ); + } + + object x = propertyValues["X"]; + object y = propertyValues["Y"]; + + if (x == null || y == null || + !(x is int) || !(y is int)) { + throw new ArgumentException(SR.PropertyValueInvalidEntry); + } + + + return new Point((int)x, + (int)y); + + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Point), attributes); + return props.Sort(new string[] {"X", "Y"}); + } + + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/DefaultPrintController.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/DefaultPrintController.cs new file mode 100644 index 00000000000..1a78f2e4ebe --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/DefaultPrintController.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Internal; +using System.Runtime.InteropServices; + +namespace System.Drawing.Printing +{ + /// + /// Specifies a print controller that sends information to a printer. + /// + public class StandardPrintController : PrintController + { + private DeviceContext? _dc; + private Graphics? _graphics; + + /// + /// Implements StartPrint for printing to a physical printer. + /// + public override void OnStartPrint(PrintDocument document, PrintEventArgs e) + { + Debug.Assert(_dc == null && _graphics == null, "PrintController methods called in the wrong order?"); + + base.OnStartPrint(document, e); + // the win32 methods below SuppressUnmanagedCodeAttributes so assertin on UnmanagedCodePermission is redundant + if (!document.PrinterSettings.IsValid) + throw new InvalidPrinterException(document.PrinterSettings); + + Debug.Assert(_modeHandle != null, "_modeHandle should have been set by PrintController.OnStartPrint"); + _dc = document.PrinterSettings.CreateDeviceContext(_modeHandle); + Interop.Gdi32.DOCINFO info = new Interop.Gdi32.DOCINFO(); + info.lpszDocName = document.DocumentName; + if (document.PrinterSettings.PrintToFile) + info.lpszOutput = document.PrinterSettings.OutputPort; //This will be "FILE:" + else + info.lpszOutput = null; + info.lpszDatatype = null; + info.fwType = 0; + + int result = Interop.Gdi32.StartDoc(new HandleRef(_dc, _dc.Hdc), info); + if (result <= 0) + { + int error = Marshal.GetLastPInvokeError(); + if (error == SafeNativeMethods.ERROR_CANCELLED) + { + e.Cancel = true; + } + else + { + throw new Win32Exception(error); + } + } + } + + /// + /// Implements StartPage for printing to a physical printer. + /// + public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) + { + Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?"); + Debug.Assert(_modeHandle != null); + + base.OnStartPage(document, e); + e.PageSettings.CopyToHdevmode(_modeHandle); + IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, _modeHandle)); + try + { + IntPtr result = Interop.Gdi32.ResetDC(new HandleRef(_dc, _dc.Hdc), new HandleRef(null, modePointer)); + Debug.Assert(result == _dc.Hdc, "ResetDC didn't return the same handle I gave it"); + } + finally + { + Interop.Kernel32.GlobalUnlock(new HandleRef(this, _modeHandle)); + } + + _graphics = Graphics.FromHdcInternal(_dc.Hdc); + + if (document.OriginAtMargins) + { + // Adjust the origin of the graphics object to be at the + // user-specified margin location + // + int dpiX = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSX); + int dpiY = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSY); + int hardMarginX_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX); + int hardMarginY_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY); + float hardMarginX = hardMarginX_DU * 100 / dpiX; + float hardMarginY = hardMarginY_DU * 100 / dpiY; + + _graphics.TranslateTransform(-hardMarginX, -hardMarginY); + _graphics.TranslateTransform(document.DefaultPageSettings.Margins.Left, document.DefaultPageSettings.Margins.Top); + } + + + int result2 = Interop.Gdi32.StartPage(new HandleRef(_dc, _dc.Hdc)); + if (result2 <= 0) + throw new Win32Exception(); + return _graphics; + } + + /// + /// Implements EndPage for printing to a physical printer. + /// + public override void OnEndPage(PrintDocument document, PrintPageEventArgs e) + { + Debug.Assert(_dc != null && _graphics != null, "PrintController methods called in the wrong order?"); + + try + { + int result = Interop.Gdi32.EndPage(new HandleRef(_dc, _dc.Hdc)); + if (result <= 0) + throw new Win32Exception(); + } + finally + { + _graphics.Dispose(); // Dispose of GDI+ Graphics; keep the DC + _graphics = null; + } + base.OnEndPage(document, e); + } + + /// + /// Implements EndPrint for printing to a physical printer. + /// + public override void OnEndPrint(PrintDocument document, PrintEventArgs e) + { + Debug.Assert(_dc != null && _graphics == null, "PrintController methods called in the wrong order?"); + + if (_dc != null) + { + try + { + int result = (e.Cancel) ? Interop.Gdi32.AbortDoc(new HandleRef(_dc, _dc.Hdc)) : Interop.Gdi32.EndDoc(new HandleRef(_dc, _dc.Hdc)); + if (result <= 0) + throw new Win32Exception(); + } + finally + { + _dc.Dispose(); + _dc = null; + } + } + + base.OnEndPrint(document, e); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/Duplex.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/Duplex.cs new file mode 100644 index 00000000000..2cafdd74cec --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/Duplex.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies the printer's duplex setting. + /// + public enum Duplex + { + /// + /// The printer's default duplex setting. + /// + Default = -1, + + /// + /// Single-sided printing. + /// + Simplex = SafeNativeMethods.DMDUP_SIMPLEX, + + /// + /// Double-sided, horizontal printing. + /// + Horizontal = SafeNativeMethods.DMDUP_HORIZONTAL, + + /// + /// Double-sided, vertical printing. + /// + Vertical = SafeNativeMethods.DMDUP_VERTICAL, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs new file mode 100644 index 00000000000..4f8e6b67dc2 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Core.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.Serialization; + +/* + * This file is not intended to be used by Mono. + * Instead InvalidPrinterException.Serializable.cs should be used. + */ + +namespace System.Drawing.Printing +{ + [System.Runtime.CompilerServices.TypeForwardedFrom("System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")] + public partial class InvalidPrinterException + { + protected InvalidPrinterException(SerializationInfo info, StreamingContext context) : base(info, context) + { + // Ignoring not deserializable input + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("settings", null); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs new file mode 100644 index 00000000000..603bd2ef438 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.Serializable.cs @@ -0,0 +1,23 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file isn't built into the .csproj in the runtime libraries but is consumed by Mono. + +using System.Runtime.Serialization; + +namespace System.Drawing.Printing +{ + partial class InvalidPrinterException + { + protected InvalidPrinterException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _settings = (PrinterSettings)info.GetValue("settings", typeof(PrinterSettings)); + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + info.AddValue("settings", _settings); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs new file mode 100644 index 00000000000..5a9979fe9e6 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/InvalidPrinterException.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security; + +namespace System.Drawing.Printing +{ + /// + /// Represents the exception that is thrown when trying to access a printer using invalid printer settings. + /// + [Serializable] + public partial class InvalidPrinterException : SystemException + { + private readonly PrinterSettings? _settings; + + /// + /// Initializes a new instance of the class. + /// + public InvalidPrinterException(PrinterSettings settings) + : base(GenerateMessage(settings)) + { + _settings = settings; + } + + private static string GenerateMessage(PrinterSettings settings) + { + if (settings.IsDefaultPrinter) + { + return SR.InvalidPrinterException_NoDefaultPrinter; + } + else + { + try + { + return SR.Format(SR.InvalidPrinterException_InvalidPrinter, settings.PrinterName); + } + catch (SecurityException) + { + return SR.Format(SR.InvalidPrinterException_InvalidPrinter, SR.CantTellPrinterName); + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.Serializable.cs new file mode 100644 index 00000000000..dddcf62f2cb --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.Serializable.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file isn't built into the .csproj in the runtime libraries but is consumed by Mono. + +using System.Runtime.Serialization; + +namespace System.Drawing.Printing +{ + [Serializable] + partial class Margins + { + [OnDeserialized] + private void OnDeserializedMethod(StreamingContext context) + { + if (_doubleLeft == 0 && _left != 0) + { + _doubleLeft = (double)_left; + } + + if (_doubleRight == 0 && _right != 0) + { + _doubleRight = (double)_right; + } + + if (_doubleTop == 0 && _top != 0) + { + _doubleTop = (double)_top; + } + + if (_doubleBottom == 0 && _bottom != 0) + { + _doubleBottom = (double)_bottom; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs new file mode 100644 index 00000000000..bcf82122dc5 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/Margins.cs @@ -0,0 +1,240 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.Serialization; + +namespace System.Drawing.Printing +{ + /// + /// Specifies the margins of a printed page. + /// + [TypeConverter(typeof(MarginsConverter))] + public partial class Margins : ICloneable + { + private int _left; + private int _right; + private int _bottom; + private int _top; + + [OptionalField] + private double _doubleLeft; + + [OptionalField] + private double _doubleRight; + + [OptionalField] + private double _doubleTop; + + [OptionalField] + private double _doubleBottom; + + /// + /// Initializes a new instance of a the class with one-inch margins. + /// + public Margins() : this(100, 100, 100, 100) + { + } + + /// + /// Initializes a new instance of a the class with the specified left, right, top, and bottom margins. + /// + public Margins(int left, int right, int top, int bottom) + { + CheckMargin(left, nameof(left)); + CheckMargin(right, nameof(right)); + CheckMargin(top, nameof(top)); + CheckMargin(bottom, nameof(bottom)); + + _left = left; + _right = right; + _top = top; + _bottom = bottom; + + _doubleLeft = (double)left; + _doubleRight = (double)right; + _doubleTop = (double)top; + _doubleBottom = (double)bottom; + } + + /// + /// Gets or sets the left margin, in hundredths of an inch. + /// + public int Left + { + get => _left; + set + { + CheckMargin(value, nameof(value)); + _left = value; + _doubleLeft = (double)value; + } + } + + /// + /// Gets or sets the right margin, in hundredths of an inch. + /// + public int Right + { + get => _right; + set + { + CheckMargin(value, nameof(value)); + _right = value; + _doubleRight = (double)value; + } + } + + /// + /// Gets or sets the top margin, in hundredths of an inch. + /// + public int Top + { + get => _top; + set + { + CheckMargin(value, nameof(value)); + _top = value; + _doubleTop = (double)value; + } + } + + /// + /// Gets or sets the bottom margin, in hundredths of an inch. + /// + public int Bottom + { + get => _bottom; + set + { + CheckMargin(value, nameof(value)); + _bottom = value; + _doubleBottom = (double)value; + } + } + + /// + /// Gets or sets the left margin with double value, in hundredths of an inch. + /// When use the setter, the ranger of setting double value should between + /// 0 to Int.MaxValue; + /// + internal double DoubleLeft + { + get => _doubleLeft; + set + { + Left = (int)Math.Round(value); + _doubleLeft = value; + } + } + + /// + /// Gets or sets the right margin with double value, in hundredths of an inch. + /// When use the setter, the ranger of setting double value should between + /// 0 to Int.MaxValue; + /// + internal double DoubleRight + { + get => _doubleRight; + set + { + Right = (int)Math.Round(value); + _doubleRight = value; + } + } + + /// + /// Gets or sets the top margin with double value, in hundredths of an inch. + /// When use the setter, the ranger of setting double value should between + /// 0 to Int.MaxValue; + /// + internal double DoubleTop + { + get => _doubleTop; + set + { + Top = (int)Math.Round(value); + _doubleTop = value; + } + } + + /// + /// Gets or sets the bottom margin with double value, in hundredths of an inch. + /// When use the setter, the ranger of setting double value should between + /// 0 to Int.MaxValue; + /// + internal double DoubleBottom + { + get => _doubleBottom; + set + { + Bottom = (int)Math.Round(value); + _doubleBottom = value; + } + } + + private static void CheckMargin(int margin, string name) + { + if (margin < 0) + { + throw new ArgumentOutOfRangeException(name, margin, SR.Format(SR.InvalidLowBoundArgumentEx, name, margin, 0)); + } + } + + /// + /// Retrieves a duplicate of this object, member by member. + /// + public object Clone() => MemberwiseClone(); + + /// + /// Compares this to a specified to see whether they + /// are equal. + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + if (!(obj is Margins margins)) + { + return false; + } + + return margins.Left == Left + && margins.Right == Right + && margins.Top == Top + && margins.Bottom == Bottom; + } + + /// + /// Calculates and retrieves a hash code based on the left, right, top, and bottom margins. + /// + public override int GetHashCode() => HashCode.Combine(Left, Right, Top, Bottom); + + /// + /// Tests whether two objects are identical. + /// + public static bool operator ==(Margins? m1, Margins? m2) + { + if (m1 is null) + { + return m2 is null; + } + if (m2 is null) + { + return false; + } + + return m1.Equals(m2); + } + + /// + /// Tests whether two objects are different. + /// + public static bool operator !=(Margins? m1, Margins? m2) => !(m1 == m2); + + /// + /// Provides some interesting information for the Margins in String form. + /// + public override string ToString() => $"[Margins Left={Left} Right={Right} Top={Top} Bottom={Bottom}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs new file mode 100644 index 00000000000..30836c4470e --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/MarginsConverter.cs @@ -0,0 +1,160 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; + +namespace System.Drawing.Printing +{ + /// + /// Provides a type converter to convert to and from various other representations, such as a string. + /// + public class MarginsConverter : ExpandableObjectConverter + { + /// + /// Determines if a converter can convert an object of the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) + { + if (destinationType == typeof(InstanceDescriptor)) + { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// Converts the given object to the converter's native type. + /// + public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) + { + if (value is string strValue) + { + string text = strValue.Trim(); + + if (text.Length == 0) + { + return null; + } + else + { + // Parse 4 integer values. + culture ??= CultureInfo.CurrentCulture; + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(sep); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = GetIntConverter(); + for (int i = 0; i < values.Length; i++) + { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i])!; + } + if (values.Length != 4) + { + throw new ArgumentException(SR.Format(SR.TextParseFailedFormat, text, "left, right, top, bottom")); + } + return new Margins(values[0], values[1], values[2], values[3]); + } + } + return base.ConvertFrom(context, culture, value); + } + + [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + Justification = "TypeDescriptor.GetConverter is safe for primitive types.")] + private static TypeConverter GetIntConverter() => TypeDescriptor.GetConverter(typeof(int)); + + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the destination type, this will + /// throw a NotSupportedException. + /// + public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) + { + ArgumentNullException.ThrowIfNull(destinationType); + + if (value is Margins margins) + { + if (destinationType == typeof(string)) + { + culture ??= CultureInfo.CurrentCulture; + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = GetIntConverter(); + string?[] args = new string[4]; + int nArg = 0; + + // Note: ConvertToString will raise exception if value cannot be converted. + args[nArg++] = intConverter.ConvertToString(context, culture, margins.Left); + args[nArg++] = intConverter.ConvertToString(context, culture, margins.Right); + args[nArg++] = intConverter.ConvertToString(context, culture, margins.Top); + args[nArg++] = intConverter.ConvertToString(context, culture, margins.Bottom); + + return string.Join(sep, args); + } + if (destinationType == typeof(InstanceDescriptor)) + { + ConstructorInfo? ctor = typeof(Margins).GetConstructor(new Type[] { + typeof(int), typeof(int), typeof(int), typeof(int)}); + + if (ctor != null) + { + return new InstanceDescriptor(ctor, new object[] { + margins.Left, margins.Right, margins.Top, margins.Bottom}); + } + } + } + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context) => true; + + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues) + { + ArgumentNullException.ThrowIfNull(propertyValues); + + object? left = propertyValues["Left"]; + object? right = propertyValues["Right"]; + object? top = propertyValues["Top"]; + object? bottom = propertyValues["Bottom"]; + + if (left == null || right == null || bottom == null || top == null || + !(left is int) || !(right is int) || !(bottom is int) || !(top is int)) + { + throw new ArgumentException(SR.PropertyValueInvalidEntry); + } + + return new Margins((int)left, + (int)right, + (int)top, + (int)bottom); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/ModeField.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/ModeField.cs new file mode 100644 index 00000000000..746eae41576 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/ModeField.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + // Some of the fields in DEVMODE + internal enum ModeField + { + Orientation, + PaperSize, + PaperLength, + PaperWidth, + Copies, + DefaultSource, + PrintQuality, + Color, + Duplex, + YResolution, + TTOption, + Collate, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Serializable.cs new file mode 100644 index 00000000000..142be6e690a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.Serializable.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + [Serializable] + partial class PageSettings + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.cs new file mode 100644 index 00000000000..0c043e5d6b3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PageSettings.cs @@ -0,0 +1,537 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.Runtime.InteropServices; + +namespace System.Drawing.Printing +{ + /// + /// Specifies settings that apply to a single page. + /// + public partial class PageSettings : ICloneable + { + internal PrinterSettings printerSettings; + + private TriState _color = TriState.Default; + private PaperSize? _paperSize; + private PaperSource? _paperSource; + private PrinterResolution? _printerResolution; + private TriState _landscape = TriState.Default; + private Margins _margins = new Margins(); + + /// + /// Initializes a new instance of the class using the default printer. + /// + public PageSettings() : this(new PrinterSettings()) + { + } + + /// + /// Initializes a new instance of the class using the specified printer. + /// + public PageSettings(PrinterSettings printerSettings) + { + Debug.Assert(printerSettings != null, "printerSettings == null"); + this.printerSettings = printerSettings; + } + + /// + /// Gets the bounds of the page, taking into account the Landscape property. + /// + public Rectangle Bounds + { + get + { + IntPtr modeHandle = printerSettings.GetHdevmode(); + Rectangle pageBounds = GetBounds(modeHandle); + + Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle)); + return pageBounds; + } + } + + /// + /// Gets or sets a value indicating whether the page is printed in color. + /// + public bool Color + { + get + { + if (_color.IsDefault) + return printerSettings.GetModeField(ModeField.Color, SafeNativeMethods.DMCOLOR_MONOCHROME) == SafeNativeMethods.DMCOLOR_COLOR; + else + return (bool)_color; + } + set { _color = value; } + } + + /// + /// Returns the x dimension of the hard margin + /// + public float HardMarginX + { + get + { + float hardMarginX = 0; + DeviceContext dc = printerSettings.CreateDeviceContext(this); + + try + { + int dpiX = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSX); + int hardMarginX_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX); + hardMarginX = hardMarginX_DU * 100 / dpiX; + } + finally + { + dc.Dispose(); + } + return hardMarginX; + } + } + + + /// + /// Returns the y dimension of the hard margin. + /// + public float HardMarginY + { + get + { + float hardMarginY = 0; + DeviceContext dc = printerSettings.CreateDeviceContext(this); + + try + { + int dpiY = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSY); + int hardMarginY_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY); + hardMarginY = hardMarginY_DU * 100 / dpiY; + } + finally + { + dc.Dispose(); + } + return hardMarginY; + } + } + + /// + /// Gets or sets a value indicating whether the page should be printed in landscape or portrait orientation. + /// + public bool Landscape + { + get + { + if (_landscape.IsDefault) + return printerSettings.GetModeField(ModeField.Orientation, SafeNativeMethods.DMORIENT_PORTRAIT) == SafeNativeMethods.DMORIENT_LANDSCAPE; + else + return (bool)_landscape; + } + set { _landscape = value; } + } + + /// + /// Gets or sets a value indicating the margins for this page. + /// + public Margins Margins + { + get { return _margins; } + set { _margins = value; } + } + + /// + /// Gets or sets the paper size. + /// + public PaperSize PaperSize + { + get + { + return GetPaperSize(IntPtr.Zero); + } + set { _paperSize = value; } + } + + /// + /// Gets or sets a value indicating the paper source (i.e. upper bin). + /// + public PaperSource PaperSource + { + get + { + if (_paperSource == null) + { + IntPtr modeHandle = printerSettings.GetHdevmode(); + IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle)); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + + PaperSource result = PaperSourceFromMode(mode); + + Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle)); + Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle)); + + return result; + } + else + return _paperSource; + } + set { _paperSource = value; } + } + + /// + /// Gets the PrintableArea for the printer. Units = 100ths of an inch. + /// + public RectangleF PrintableArea + { + get + { + RectangleF printableArea = default; + DeviceContext dc = printerSettings.CreateInformationContext(this); + HandleRef hdc = new HandleRef(dc, dc.Hdc); + + try + { + int dpiX = Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.LOGPIXELSX); + int dpiY = Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.LOGPIXELSY); + if (!Landscape) + { + // + // Need to convert the printable area to 100th of an inch from the device units + printableArea.X = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX) * 100 / dpiX; + printableArea.Y = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY) * 100 / dpiY; + printableArea.Width = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.HORZRES) * 100 / dpiX; + printableArea.Height = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.VERTRES) * 100 / dpiY; + } + else + { + // + // Need to convert the printable area to 100th of an inch from the device units + printableArea.Y = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX) * 100 / dpiX; + printableArea.X = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY) * 100 / dpiY; + printableArea.Height = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.HORZRES) * 100 / dpiX; + printableArea.Width = (float)Interop.Gdi32.GetDeviceCaps(hdc, Interop.Gdi32.DeviceCapability.VERTRES) * 100 / dpiY; + } + } + finally + { + dc.Dispose(); + } + + return printableArea; + } + } + + /// + /// Gets or sets the printer resolution for the page. + /// + public PrinterResolution PrinterResolution + { + get + { + if (_printerResolution == null) + { + IntPtr modeHandle = printerSettings.GetHdevmode(); + IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle)); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + + PrinterResolution result = PrinterResolutionFromMode(mode); + + Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle)); + Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle)); + + return result; + } + else + return _printerResolution; + } + set + { + _printerResolution = value; + } + } + + /// + /// Gets or sets the associated printer settings. + /// + public PrinterSettings PrinterSettings + { + get => printerSettings; + set => printerSettings = value ?? new PrinterSettings(); + } + + /// + /// Copies the settings and margins. + /// + public object Clone() + { + PageSettings result = (PageSettings)MemberwiseClone(); + result._margins = (Margins)_margins.Clone(); + return result; + } + + /// + /// Copies the relevant information out of the PageSettings and into the handle. + /// + public void CopyToHdevmode(IntPtr hdevmode) + { + IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + + if (_color.IsNotDefault && ((mode.dmFields & SafeNativeMethods.DM_COLOR) == SafeNativeMethods.DM_COLOR)) + mode.dmColor = unchecked((short)(((bool)_color) ? SafeNativeMethods.DMCOLOR_COLOR : SafeNativeMethods.DMCOLOR_MONOCHROME)); + if (_landscape.IsNotDefault && ((mode.dmFields & SafeNativeMethods.DM_ORIENTATION) == SafeNativeMethods.DM_ORIENTATION)) + mode.dmOrientation = unchecked((short)(((bool)_landscape) ? SafeNativeMethods.DMORIENT_LANDSCAPE : SafeNativeMethods.DMORIENT_PORTRAIT)); + + if (_paperSize != null) + { + if ((mode.dmFields & SafeNativeMethods.DM_PAPERSIZE) == SafeNativeMethods.DM_PAPERSIZE) + { + mode.dmPaperSize = unchecked((short)_paperSize.RawKind); + } + + bool setWidth = false; + bool setLength = false; + + if ((mode.dmFields & SafeNativeMethods.DM_PAPERLENGTH) == SafeNativeMethods.DM_PAPERLENGTH) + { + // dmPaperLength is always in tenths of millimeter but paperSizes are in hundredth of inch .. + // so we need to convert :: use PrinterUnitConvert.Convert(value, PrinterUnit.TenthsOfAMillimeter /*fromUnit*/, PrinterUnit.Display /*ToUnit*/) + int length = PrinterUnitConvert.Convert(_paperSize.Height, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); + mode.dmPaperLength = unchecked((short)length); + setLength = true; + } + if ((mode.dmFields & SafeNativeMethods.DM_PAPERWIDTH) == SafeNativeMethods.DM_PAPERWIDTH) + { + int width = PrinterUnitConvert.Convert(_paperSize.Width, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); + mode.dmPaperWidth = unchecked((short)width); + setWidth = true; + } + + if (_paperSize.Kind == PaperKind.Custom) + { + if (!setLength) + { + mode.dmFields |= SafeNativeMethods.DM_PAPERLENGTH; + int length = PrinterUnitConvert.Convert(_paperSize.Height, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); + mode.dmPaperLength = unchecked((short)length); + } + if (!setWidth) + { + mode.dmFields |= SafeNativeMethods.DM_PAPERWIDTH; + int width = PrinterUnitConvert.Convert(_paperSize.Width, PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter); + mode.dmPaperWidth = unchecked((short)width); + } + } + } + + if (_paperSource != null && ((mode.dmFields & SafeNativeMethods.DM_DEFAULTSOURCE) == SafeNativeMethods.DM_DEFAULTSOURCE)) + { + mode.dmDefaultSource = unchecked((short)_paperSource.RawKind); + } + + if (_printerResolution != null) + { + if (_printerResolution.Kind == PrinterResolutionKind.Custom) + { + if ((mode.dmFields & SafeNativeMethods.DM_PRINTQUALITY) == SafeNativeMethods.DM_PRINTQUALITY) + { + mode.dmPrintQuality = unchecked((short)_printerResolution.X); + } + if ((mode.dmFields & SafeNativeMethods.DM_YRESOLUTION) == SafeNativeMethods.DM_YRESOLUTION) + { + mode.dmYResolution = unchecked((short)_printerResolution.Y); + } + } + else + { + if ((mode.dmFields & SafeNativeMethods.DM_PRINTQUALITY) == SafeNativeMethods.DM_PRINTQUALITY) + { + mode.dmPrintQuality = unchecked((short)_printerResolution.Kind); + } + } + } + + Marshal.StructureToPtr(mode, modePointer, false); + + // It's possible this page has a DEVMODE for a different printer than the DEVMODE passed in here + // (Ex: occurs when Doc.DefaultPageSettings.PrinterSettings.PrinterName != Doc.PrinterSettings.PrinterName) + // + // if the passed in devmode has fewer bytes than our buffer for the extrainfo, we want to skip the merge as it will cause + // a buffer overrun + if (mode.dmDriverExtra >= ExtraBytes) + { + int retCode = Interop.Winspool.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printerSettings.PrinterName, modePointer, modePointer, SafeNativeMethods.DM_IN_BUFFER | SafeNativeMethods.DM_OUT_BUFFER); + if (retCode < 0) + { + Interop.Kernel32.GlobalFree(modePointer); + } + } + + Interop.Kernel32.GlobalUnlock(hdevmode); + } + + private short ExtraBytes + { + get + { + IntPtr modeHandle = printerSettings.GetHdevmodeInternal(); + IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle)); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + + short result = mode?.dmDriverExtra ?? 0; + + Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle)); + Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle)); + + return result; + } + } + + + // This function shows up big on profiles, so we need to make it fast + internal Rectangle GetBounds(IntPtr modeHandle) + { + Rectangle pageBounds; + PaperSize size = GetPaperSize(modeHandle); + if (GetLandscape(modeHandle)) + pageBounds = new Rectangle(0, 0, size.Height, size.Width); + else + pageBounds = new Rectangle(0, 0, size.Width, size.Height); + + return pageBounds; + } + + private bool GetLandscape(IntPtr modeHandle) + { + if (_landscape.IsDefault) + return printerSettings.GetModeField(ModeField.Orientation, SafeNativeMethods.DMORIENT_PORTRAIT, modeHandle) == SafeNativeMethods.DMORIENT_LANDSCAPE; + else + return (bool)_landscape; + } + + private PaperSize GetPaperSize(IntPtr modeHandle) + { + if (_paperSize == null) + { + bool ownHandle = false; + if (modeHandle == IntPtr.Zero) + { + modeHandle = printerSettings.GetHdevmode(); + ownHandle = true; + } + + IntPtr modePointer = Interop.Kernel32.GlobalLock(modeHandle); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + + PaperSize result = PaperSizeFromMode(mode); + + Interop.Kernel32.GlobalUnlock(modeHandle); + + if (ownHandle) + { + Interop.Kernel32.GlobalFree(modeHandle); + } + + return result; + } + else + return _paperSize; + } + + private PaperSize PaperSizeFromMode(Interop.Gdi32.DEVMODE mode) + { + PaperSize[] sizes = printerSettings.Get_PaperSizes(); + if ((mode.dmFields & SafeNativeMethods.DM_PAPERSIZE) == SafeNativeMethods.DM_PAPERSIZE) + { + for (int i = 0; i < sizes.Length; i++) + { + if ((int)sizes[i].RawKind == mode.dmPaperSize) + return sizes[i]; + } + } + return new PaperSize(PaperKind.Custom, "custom", + //mode.dmPaperWidth, mode.dmPaperLength); + PrinterUnitConvert.Convert(mode.dmPaperWidth, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display), + PrinterUnitConvert.Convert(mode.dmPaperLength, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display)); + } + + private PaperSource PaperSourceFromMode(Interop.Gdi32.DEVMODE mode) + { + PaperSource[] sources = printerSettings.Get_PaperSources(); + if ((mode.dmFields & SafeNativeMethods.DM_DEFAULTSOURCE) == SafeNativeMethods.DM_DEFAULTSOURCE) + { + for (int i = 0; i < sources.Length; i++) + { + // the dmDefaultSource == to the RawKind in the Papersource.. and Not the Kind... + // if the PaperSource is populated with CUSTOM values... + if (unchecked((short)sources[i].RawKind) == mode.dmDefaultSource) + { + return sources[i]; + } + } + } + return new PaperSource((PaperSourceKind)mode.dmDefaultSource, "unknown"); + } + + private PrinterResolution PrinterResolutionFromMode(Interop.Gdi32.DEVMODE mode) + { + PrinterResolution[] resolutions = printerSettings.Get_PrinterResolutions(); + for (int i = 0; i < resolutions.Length; i++) + { + if (mode.dmPrintQuality >= 0 && ((mode.dmFields & SafeNativeMethods.DM_PRINTQUALITY) == SafeNativeMethods.DM_PRINTQUALITY) + && ((mode.dmFields & SafeNativeMethods.DM_YRESOLUTION) == SafeNativeMethods.DM_YRESOLUTION)) + { + if (resolutions[i].X == unchecked((int)(PrinterResolutionKind)mode.dmPrintQuality) + && resolutions[i].Y == unchecked((int)(PrinterResolutionKind)mode.dmYResolution)) + return resolutions[i]; + } + else + { + if ((mode.dmFields & SafeNativeMethods.DM_PRINTQUALITY) == SafeNativeMethods.DM_PRINTQUALITY) + { + if (resolutions[i].Kind == (PrinterResolutionKind)mode.dmPrintQuality) + return resolutions[i]; + } + } + } + return new PrinterResolution(PrinterResolutionKind.Custom, + mode.dmPrintQuality, mode.dmYResolution); + } + + /// + /// Copies the relevant information out of the handle and into the PageSettings. + /// + public void SetHdevmode(IntPtr hdevmode) + { + if (hdevmode == IntPtr.Zero) + { + throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevmode)); + } + + IntPtr pointer = Interop.Kernel32.GlobalLock(hdevmode); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(pointer)!; + + if ((mode.dmFields & SafeNativeMethods.DM_COLOR) == SafeNativeMethods.DM_COLOR) + { + _color = (mode.dmColor == SafeNativeMethods.DMCOLOR_COLOR); + } + + if ((mode.dmFields & SafeNativeMethods.DM_ORIENTATION) == SafeNativeMethods.DM_ORIENTATION) + { + _landscape = (mode.dmOrientation == SafeNativeMethods.DMORIENT_LANDSCAPE); + } + + _paperSize = PaperSizeFromMode(mode); + _paperSource = PaperSourceFromMode(mode); + _printerResolution = PrinterResolutionFromMode(mode); + + Interop.Kernel32.GlobalUnlock(hdevmode); + } + + /// + /// Provides some interesting information about the PageSettings in String form. + /// + public override string ToString() => + $"[{nameof(PageSettings)}: Color={Color}, Landscape={Landscape}, Margins={Margins}, PaperSize={PaperSize}, PaperSource={PaperSource}, PrinterResolution={PrinterResolution}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperKinds.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperKinds.cs new file mode 100644 index 00000000000..b04682c64cd --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperKinds.cs @@ -0,0 +1,490 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies the standard paper sizes. + /// + public enum PaperKind + { + /// + /// The paper size is defined by the user. + /// + Custom = 0, + + // I got this information from two places: MSDN's writeup of DEVMODE + // (https://docs.microsoft.com/en-us/windows/desktop/api/wingdi/ns-wingdi-_devicemodea), + // and the raw C++ header file (wingdi.h). Beyond that, your guess + // is as good as mine as to what these members mean. + + /// + /// Letter paper (8.5 in. by 11 in.). + /// + Letter = SafeNativeMethods.DMPAPER_LETTER, + /// + /// Legal paper (8.5 in. by 14 in.). + /// + Legal = SafeNativeMethods.DMPAPER_LEGAL, + /// + /// A4 paper (210 mm by 297 mm). + /// + A4 = SafeNativeMethods.DMPAPER_A4, + /// + /// C paper (17 in. by 22 in.). + /// + CSheet = SafeNativeMethods.DMPAPER_CSHEET, + /// + /// D paper (22 in. by 34 in.). + /// + DSheet = SafeNativeMethods.DMPAPER_DSHEET, + /// + /// E paper (34 in. by 44 in.). + /// + ESheet = SafeNativeMethods.DMPAPER_ESHEET, + /// + /// Letter small paper (8.5 in. by 11 in.). + /// + LetterSmall = SafeNativeMethods.DMPAPER_LETTERSMALL, + /// + /// Tabloid paper (11 in. by 17 in.). + /// + Tabloid = SafeNativeMethods.DMPAPER_TABLOID, + /// + /// Ledger paper (17 in. by 11 in.). + /// + Ledger = SafeNativeMethods.DMPAPER_LEDGER, + /// + /// Statement paper (5.5 in. by 8.5 in.). + /// + Statement = SafeNativeMethods.DMPAPER_STATEMENT, + /// + /// Executive paper (7.25 in. by 10.5 in.). + /// + Executive = SafeNativeMethods.DMPAPER_EXECUTIVE, + /// + /// A3 paper (297 mm by 420 mm). + /// + A3 = SafeNativeMethods.DMPAPER_A3, + /// + /// A4 small paper (210 mm by 297 mm). + /// + A4Small = SafeNativeMethods.DMPAPER_A4SMALL, + /// + /// A5 paper (148 mm by 210 mm). + /// + A5 = SafeNativeMethods.DMPAPER_A5, + /// + /// B4 paper (250 mm by 353 mm). + /// + B4 = SafeNativeMethods.DMPAPER_B4, + /// + /// B5 paper (176 mm by 250 mm). + /// + B5 = SafeNativeMethods.DMPAPER_B5, + /// + /// Folio paper (8.5 in. by 13 in.). + /// + Folio = SafeNativeMethods.DMPAPER_FOLIO, + /// + /// Quarto paper (215 mm by 275 mm). + /// + Quarto = SafeNativeMethods.DMPAPER_QUARTO, + /// + /// 10-by-14-inch paper. + /// + Standard10x14 = SafeNativeMethods.DMPAPER_10X14, + /// + /// 11-by-17-inch paper. + /// + Standard11x17 = SafeNativeMethods.DMPAPER_11X17, + /// + /// Note paper (8.5 in. by 11 in.). + /// + Note = SafeNativeMethods.DMPAPER_NOTE, + /// + /// #9 envelope (3.875 in. by 8.875 in.). + /// + Number9Envelope = SafeNativeMethods.DMPAPER_ENV_9, + /// + /// #10 envelope (4.125 in. by 9.5 in.). + /// + Number10Envelope = SafeNativeMethods.DMPAPER_ENV_10, + /// + /// #11 envelope (4.5 in. by 10.375 in.). + /// + Number11Envelope = SafeNativeMethods.DMPAPER_ENV_11, + /// + /// #12 envelope (4.75 in. by 11 in.). + /// + Number12Envelope = SafeNativeMethods.DMPAPER_ENV_12, + /// + /// #14 envelope (5 in. by 11.5 in.). + /// + Number14Envelope = SafeNativeMethods.DMPAPER_ENV_14, + /// + /// DL envelope (110 mm by 220 mm). + /// + DLEnvelope = SafeNativeMethods.DMPAPER_ENV_DL, + /// + /// C5 envelope (162 mm by 229 mm). + /// + C5Envelope = SafeNativeMethods.DMPAPER_ENV_C5, + /// + /// C3 envelope (324 mm by 458 mm). + /// + C3Envelope = SafeNativeMethods.DMPAPER_ENV_C3, + /// + /// C4 envelope (229 mm by 324 mm). + /// + C4Envelope = SafeNativeMethods.DMPAPER_ENV_C4, + /// + /// C6 envelope (114 mm by 162 mm). + /// + C6Envelope = SafeNativeMethods.DMPAPER_ENV_C6, + /// + /// C65 envelope (114 mm by 229 mm). + /// + C65Envelope = SafeNativeMethods.DMPAPER_ENV_C65, + /// + /// B4 envelope (250 mm by 353 mm). + /// + B4Envelope = SafeNativeMethods.DMPAPER_ENV_B4, + /// + /// B5 envelope (176 mm by 250 mm). + /// + B5Envelope = SafeNativeMethods.DMPAPER_ENV_B5, + /// + /// B6 envelope (176 mm by 125 mm). + /// + B6Envelope = SafeNativeMethods.DMPAPER_ENV_B6, + /// + /// Italy envelope (110 mm by 230 mm). + /// + ItalyEnvelope = SafeNativeMethods.DMPAPER_ENV_ITALY, + /// + /// Monarch envelope (3.875 in. by 7.5 in.). + /// + MonarchEnvelope = SafeNativeMethods.DMPAPER_ENV_MONARCH, + /// + /// 6 3/4 envelope (3.625 in. by 6.5 in.). + /// + PersonalEnvelope = SafeNativeMethods.DMPAPER_ENV_PERSONAL, + /// + /// US standard fanfold (14.875 in. by 11 in.). + /// + USStandardFanfold = SafeNativeMethods.DMPAPER_FANFOLD_US, + /// + /// German standard fanfold (8.5 in. by 12 in.). + /// + GermanStandardFanfold = SafeNativeMethods.DMPAPER_FANFOLD_STD_GERMAN, + /// + /// German legal fanfold (8.5 in. by 13 in.). + /// + GermanLegalFanfold = SafeNativeMethods.DMPAPER_FANFOLD_LGL_GERMAN, + /// + /// ISO B4 (250 mm by 353 mm). + /// + IsoB4 = SafeNativeMethods.DMPAPER_ISO_B4, + /// + /// Japanese postcard (100 mm by 148 mm). + /// + JapanesePostcard = SafeNativeMethods.DMPAPER_JAPANESE_POSTCARD, + /// + /// 9-by-11-inch paper. + /// + Standard9x11 = SafeNativeMethods.DMPAPER_9X11, + /// + /// 10-by-11-inch paper. + /// + Standard10x11 = SafeNativeMethods.DMPAPER_10X11, + /// + /// 15-by-11-inch paper. + /// + Standard15x11 = SafeNativeMethods.DMPAPER_15X11, + /// + /// Invite envelope (220 mm by 220 mm). + /// + InviteEnvelope = SafeNativeMethods.DMPAPER_ENV_INVITE, + /// + /// Letter extra paper (9.275 in. by 12 in.). + /// This value is specific to the PostScript driver and is used only by Linotronic printers in order to conserve paper. + /// + LetterExtra = SafeNativeMethods.DMPAPER_LETTER_EXTRA, + /// + /// Legal extra paper (9.275 in. by 15 in.). + /// This value is specific to the PostScript driver and is used only by Linotronic printers in order to conserve paper. + /// + LegalExtra = SafeNativeMethods.DMPAPER_LEGAL_EXTRA, + /// + /// Tabloid extra paper (11.69 in. by 18 in.). + /// This value is specific to the PostScript driver and is used only by Linotronic printers in order to conserve paper. + /// + TabloidExtra = SafeNativeMethods.DMPAPER_TABLOID_EXTRA, + /// + /// A4 extra paper (236 mm by 322 mm). + /// This value is specific to the PostScript driver and is used only by Linotronic printers in order to conserve paper. + /// + A4Extra = SafeNativeMethods.DMPAPER_A4_EXTRA, + /// + /// Letter transverse paper (8.275 in. by 11 in.). + /// + LetterTransverse = SafeNativeMethods.DMPAPER_LETTER_TRANSVERSE, + /// + /// A4 transverse paper (210 mm by 297 mm). + /// + A4Transverse = SafeNativeMethods.DMPAPER_A4_TRANSVERSE, + /// + /// Letter extra transverse paper (9.275 in. by 12 in.). + /// + LetterExtraTransverse = SafeNativeMethods.DMPAPER_LETTER_EXTRA_TRANSVERSE, + /// + /// SuperA/SuperA/A4 paper (227 mm by 356 mm). + /// + APlus = SafeNativeMethods.DMPAPER_A_PLUS, + /// + /// SuperB/SuperB/A3 paper (305 mm by 487 mm). + /// + BPlus = SafeNativeMethods.DMPAPER_B_PLUS, + /// + /// Letter plus paper (8.5 in. by 12.69 in.). + /// + LetterPlus = SafeNativeMethods.DMPAPER_LETTER_PLUS, + /// + /// A4 plus paper (210 mm by 330 mm). + /// + A4Plus = SafeNativeMethods.DMPAPER_A4_PLUS, + /// + /// A5 transverse paper (148 mm by 210 mm). + /// + A5Transverse = SafeNativeMethods.DMPAPER_A5_TRANSVERSE, + /// + /// JIS B5 transverse paper (182 mm by 257 mm). + /// + B5Transverse = SafeNativeMethods.DMPAPER_B5_TRANSVERSE, + /// + /// A3 extra paper (322 mm by 445 mm). + /// + A3Extra = SafeNativeMethods.DMPAPER_A3_EXTRA, + /// + /// A5 extra paper (174 mm by 235 mm). + /// + A5Extra = SafeNativeMethods.DMPAPER_A5_EXTRA, + /// + /// ISO B5 extra paper (201 mm by 276 mm). + /// + B5Extra = SafeNativeMethods.DMPAPER_B5_EXTRA, + /// + /// A2 paper (420 mm by 594 mm). + /// + A2 = SafeNativeMethods.DMPAPER_A2, + /// + /// A3 transverse paper (297 mm by 420 mm). + /// + A3Transverse = SafeNativeMethods.DMPAPER_A3_TRANSVERSE, + /// + /// A3 extra transverse paper (322 mm by 445 mm). + /// + A3ExtraTransverse = SafeNativeMethods.DMPAPER_A3_EXTRA_TRANSVERSE, + /// + /// Japanese double postcard (200 mm by 148mm). + /// + JapaneseDoublePostcard = SafeNativeMethods.DMPAPER_DBL_JAPANESE_POSTCARD, + /// + /// A6 paper (105 mm by 148 mm). + /// + A6 = SafeNativeMethods.DMPAPER_A6, + /// + /// Japanese Kaku #2 envelope. + /// + JapaneseEnvelopeKakuNumber2 = SafeNativeMethods.DMPAPER_JENV_KAKU2, + /// + /// Japanese Kaku #3 envelope. + /// + JapaneseEnvelopeKakuNumber3 = SafeNativeMethods.DMPAPER_JENV_KAKU3, + /// + /// Japanese Chou #3 envelope. + /// + JapaneseEnvelopeChouNumber3 = SafeNativeMethods.DMPAPER_JENV_CHOU3, + /// + /// Japanese Chou #4 envelope. + /// + JapaneseEnvelopeChouNumber4 = SafeNativeMethods.DMPAPER_JENV_CHOU4, + /// + /// Letter rotated paper (11 in. by 8.5 in.). + /// + LetterRotated = SafeNativeMethods.DMPAPER_LETTER_ROTATED, + /// + /// A3 rotated paper (420mm by 297 mm). + /// + A3Rotated = SafeNativeMethods.DMPAPER_A3_ROTATED, + /// + /// A4 rotated paper (297 mm by 210 mm). + /// + A4Rotated = SafeNativeMethods.DMPAPER_A4_ROTATED, + /// + /// A5 rotated paper (210 mm by 148 mm). + /// + A5Rotated = SafeNativeMethods.DMPAPER_A5_ROTATED, + /// + /// JIS B4 rotated paper (364 mm by 257 mm). + /// + B4JisRotated = SafeNativeMethods.DMPAPER_B4_JIS_ROTATED, + /// + /// JIS B5 rotated paper (257 mm by 182 mm). + /// + B5JisRotated = SafeNativeMethods.DMPAPER_B5_JIS_ROTATED, + /// + /// Japanese rotated postcard (148 mm by 100 mm). + /// + JapanesePostcardRotated = SafeNativeMethods.DMPAPER_JAPANESE_POSTCARD_ROTATED, + /// + /// Japanese rotated double postcard (148 mm by 200 mm). + /// + JapaneseDoublePostcardRotated = SafeNativeMethods.DMPAPER_DBL_JAPANESE_POSTCARD_ROTATED, + /// + /// A6 rotated paper (148 mm by 105 mm). + /// + A6Rotated = SafeNativeMethods.DMPAPER_A6_ROTATED, + /// + /// Japanese rotated Kaku #2 envelope. + /// + JapaneseEnvelopeKakuNumber2Rotated = SafeNativeMethods.DMPAPER_JENV_KAKU2_ROTATED, + /// + /// Japanese rotated Kaku #3 envelope. + /// + JapaneseEnvelopeKakuNumber3Rotated = SafeNativeMethods.DMPAPER_JENV_KAKU3_ROTATED, + /// + /// Japanese rotated Chou #3 envelope. + /// + JapaneseEnvelopeChouNumber3Rotated = SafeNativeMethods.DMPAPER_JENV_CHOU3_ROTATED, + /// + /// Japanese rotated Chou #4 envelope. + /// + JapaneseEnvelopeChouNumber4Rotated = SafeNativeMethods.DMPAPER_JENV_CHOU4_ROTATED, + /// + /// JIS B6 paper (128 mm by 182 mm). + /// + B6Jis = SafeNativeMethods.DMPAPER_B6_JIS, + /// + /// JIS B6 rotated paper (182 mm by 128 mm). + /// + B6JisRotated = SafeNativeMethods.DMPAPER_B6_JIS_ROTATED, + /// + /// 12-by-11-inch paper. + /// + Standard12x11 = SafeNativeMethods.DMPAPER_12X11, + /// + /// Japanese You #4 envelope. + /// + JapaneseEnvelopeYouNumber4 = SafeNativeMethods.DMPAPER_JENV_YOU4, + /// + /// Japanese You #4 rotated envelope. + /// + JapaneseEnvelopeYouNumber4Rotated = SafeNativeMethods.DMPAPER_JENV_YOU4_ROTATED, + /// + /// PRC 16K paper (146 mm by 215 mm). + /// + Prc16K = SafeNativeMethods.DMPAPER_P16K, + /// + /// PRC 32K paper (97 mm by 151 mm). + /// + Prc32K = SafeNativeMethods.DMPAPER_P32K, + /// + /// PRC 32K big paper (97 mm by 151 mm). + /// + Prc32KBig = SafeNativeMethods.DMPAPER_P32KBIG, + /// + /// PRC #1 envelope (102 mm by 165 mm). + /// + PrcEnvelopeNumber1 = SafeNativeMethods.DMPAPER_PENV_1, + /// + /// PRC #2 envelope (102 mm by 176 mm). + /// + PrcEnvelopeNumber2 = SafeNativeMethods.DMPAPER_PENV_2, + /// + /// PRC #3 envelope (125 mm by 176 mm). + /// + PrcEnvelopeNumber3 = SafeNativeMethods.DMPAPER_PENV_3, + /// + /// PRC #4 envelope (110 mm by 208 mm). + /// + PrcEnvelopeNumber4 = SafeNativeMethods.DMPAPER_PENV_4, + /// + /// PRC #5 envelope (110 mm by 220 mm). + /// + PrcEnvelopeNumber5 = SafeNativeMethods.DMPAPER_PENV_5, + /// + /// PRC #6 envelope (120 mm by 230 mm). + /// + PrcEnvelopeNumber6 = SafeNativeMethods.DMPAPER_PENV_6, + /// + /// PRC #7 envelope (160 mm by 230 mm). + /// + PrcEnvelopeNumber7 = SafeNativeMethods.DMPAPER_PENV_7, + /// + /// PRC #8 envelope (120 mm by 309 mm). + /// + PrcEnvelopeNumber8 = SafeNativeMethods.DMPAPER_PENV_8, + /// + /// PRC #9 envelope (229 mm by 324 mm). + /// + PrcEnvelopeNumber9 = SafeNativeMethods.DMPAPER_PENV_9, + /// + /// PRC #10 envelope (324 mm by 458 mm). + /// + PrcEnvelopeNumber10 = SafeNativeMethods.DMPAPER_PENV_10, + /// + /// PRC 16K rotated paper (146 mm by 215 mm). + /// + Prc16KRotated = SafeNativeMethods.DMPAPER_P16K_ROTATED, + /// + /// PRC 32K rotated paper (97 mm by 151 mm). + /// + Prc32KRotated = SafeNativeMethods.DMPAPER_P32K_ROTATED, + /// + /// PRC 32K big rotated paper (97 mm by 151 mm). + /// + Prc32KBigRotated = SafeNativeMethods.DMPAPER_P32KBIG_ROTATED, + /// + /// PRC #1 rotated envelope (165 mm by 102 mm). + /// + PrcEnvelopeNumber1Rotated = SafeNativeMethods.DMPAPER_PENV_1_ROTATED, + /// + /// PRC #2 rotated envelope (176 mm by 102 mm). + /// + PrcEnvelopeNumber2Rotated = SafeNativeMethods.DMPAPER_PENV_2_ROTATED, + /// + /// PRC #3 rotated envelope (176 mm by 125 mm). + /// + PrcEnvelopeNumber3Rotated = SafeNativeMethods.DMPAPER_PENV_3_ROTATED, + /// + /// PRC #4 rotated envelope (208 mm by 110 mm). + /// + PrcEnvelopeNumber4Rotated = SafeNativeMethods.DMPAPER_PENV_4_ROTATED, + /// + /// PRC #5 rotated envelope (220 mm by 110 mm). + /// + PrcEnvelopeNumber5Rotated = SafeNativeMethods.DMPAPER_PENV_5_ROTATED, + /// + /// PRC #6 rotated envelope (230 mm by 120 mm). + /// + PrcEnvelopeNumber6Rotated = SafeNativeMethods.DMPAPER_PENV_6_ROTATED, + /// + /// PRC #7 rotated envelope (230 mm by 160 mm). + /// + PrcEnvelopeNumber7Rotated = SafeNativeMethods.DMPAPER_PENV_7_ROTATED, + /// + /// PRC #8 rotated envelope (309 mm by 120 mm). + /// + PrcEnvelopeNumber8Rotated = SafeNativeMethods.DMPAPER_PENV_8_ROTATED, + /// + /// PRC #9 rotated envelope (324 mm by 229 mm). + /// + PrcEnvelopeNumber9Rotated = SafeNativeMethods.DMPAPER_PENV_9_ROTATED, + /// + /// PRC #10 rotated envelope (458 mm by 324 mm). + /// + PrcEnvelopeNumber10Rotated = SafeNativeMethods.DMPAPER_PENV_10_ROTATED, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.Serializable.cs new file mode 100644 index 00000000000..e19373e9728 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.Serializable.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file isn't built into the .csproj in the runtime libraries but is consumed by Mono. + +namespace System.Drawing.Printing +{ + [Serializable] + partial class PaperSize + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs new file mode 100644 index 00000000000..75e69bb466a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSize.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Globalization; + +namespace System.Drawing.Printing +{ + /// + /// Specifies the size of a piece of paper. + /// + public partial class PaperSize + { + private PaperKind _kind; + private string _name; + + // standard hundredths of an inch units + private int _width; + private int _height; + private readonly bool _createdByDefaultConstructor; + + /// + /// Initializes a new instance of the class with default properties. + /// + public PaperSize() + { + _kind = PaperKind.Custom; + _name = string.Empty; + _createdByDefaultConstructor = true; + } + + internal PaperSize(PaperKind kind, string name, int width, int height) + { + _kind = kind; + _name = name; + _width = width; + _height = height; + } + + /// + /// Initializes a new instance of the class. + /// + public PaperSize(string name, int width, int height) + { + _kind = PaperKind.Custom; + _name = name; + _width = width; + _height = height; + } + + /// + /// Gets or sets the height of the paper, in hundredths of an inch. + /// + public int Height + { + get => _height; + set + { + if (_kind != PaperKind.Custom && !_createdByDefaultConstructor) + { + throw new ArgumentException(SR.PSizeNotCustom, nameof(value)); + } + + _height = value; + } + } + + /// + /// Gets the type of paper. + /// + public PaperKind Kind + { + get + { + if (_kind <= (PaperKind)SafeNativeMethods.DMPAPER_LAST && + !(_kind == (PaperKind)SafeNativeMethods.DMPAPER_RESERVED_48 || _kind == (PaperKind)SafeNativeMethods.DMPAPER_RESERVED_49)) + { + return _kind; + } + + return PaperKind.Custom; + } + } + + /// + /// Gets or sets the name of the type of paper. + /// + public string PaperName + { + get => _name; + set + { + if (_kind != PaperKind.Custom && !_createdByDefaultConstructor) + { + throw new ArgumentException(SR.PSizeNotCustom, nameof(value)); + } + + _name = value; + } + } + + /// + /// Same as Kind, but values larger than or equal to DMPAPER_LAST do not map to PaperKind.Custom. + /// + public int RawKind + { + get => unchecked((int)_kind); + set => _kind = unchecked((PaperKind)value); + } + + /// + /// Gets or sets the width of the paper, in hundredths of an inch. + /// + public int Width + { + get => _width; + set + { + if (_kind != PaperKind.Custom && !_createdByDefaultConstructor) + { + throw new ArgumentException(SR.PSizeNotCustom, nameof(value)); + } + + _width = value; + } + } + + /// + /// Provides some interesting information about the PaperSize in String form. + /// + public override string ToString() => $"[PaperSize {PaperName} Kind={Kind} Height={Height.ToString(CultureInfo.InvariantCulture)} Width={Width.ToString(CultureInfo.InvariantCulture)}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.Serializable.cs new file mode 100644 index 00000000000..e590de765a5 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.Serializable.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file isn't built into the .csproj in the runtime libraries but is consumed by Mono. + +namespace System.Drawing.Printing +{ + [Serializable] + partial class PaperSource + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs new file mode 100644 index 00000000000..5a321cc4d81 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSource.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies the paper tray from which the printer gets paper. + /// + public partial class PaperSource + { + private string _name; + private PaperSourceKind _kind; + + /// + /// Initializes a new instance of the class with default properties. + /// + public PaperSource() + { + _kind = PaperSourceKind.Custom; + _name = string.Empty; + } + + internal PaperSource(PaperSourceKind kind, string name) + { + _kind = kind; + _name = name; + } + + /// + /// Gets a value indicating the type of paper source. + /// + public PaperSourceKind Kind + { + get + { + if ((unchecked((int)_kind)) >= SafeNativeMethods.DMBIN_USER) + { + return PaperSourceKind.Custom; + } + + return _kind; + } + } + + /// + /// Same as Kind, but values larger than DMBIN_USER do not map to PaperSourceKind.Custom. + /// + public int RawKind + { + get => unchecked((int)_kind); + set => _kind = unchecked((PaperSourceKind)value); + } + + /// + /// Gets the name of the paper source. + /// + public string SourceName + { + get => _name; + set => _name = value; + } + + /// + /// Provides some interesting information about the PaperSource in String form. + /// + public override string ToString() => $"[PaperSource {SourceName} Kind={Kind}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSourceKind.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSourceKind.cs new file mode 100644 index 00000000000..0fc82103ac8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PaperSourceKind.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Standard paper sources. + /// + public enum PaperSourceKind + { + // Please keep these in SafeNativeMethods.cs order + + /// + /// The upper bin of a printer (or, if the printer only has one bin, the only bin). + /// + Upper = SafeNativeMethods.DMBIN_UPPER, + + /// + /// The lower bin of a printer. + /// + Lower = SafeNativeMethods.DMBIN_LOWER, + + /// + /// The middle bin of a printer. + /// + Middle = SafeNativeMethods.DMBIN_MIDDLE, + + /// + /// Manually-fed paper. + /// + Manual = SafeNativeMethods.DMBIN_MANUAL, + + /// + /// An envelope. + /// + Envelope = SafeNativeMethods.DMBIN_ENVELOPE, + + /// + /// A manually-fed envelope. + /// + ManualFeed = SafeNativeMethods.DMBIN_ENVMANUAL, + + /// + /// Automatic-fed paper. + /// + AutomaticFeed = SafeNativeMethods.DMBIN_AUTO, + + /// + /// A tractor feed. + /// + TractorFeed = SafeNativeMethods.DMBIN_TRACTOR, + + /// + /// Small-format paper. + /// + SmallFormat = SafeNativeMethods.DMBIN_SMALLFMT, + + /// + /// Large-format paper. + /// + LargeFormat = SafeNativeMethods.DMBIN_LARGEFMT, + + /// + /// A large-capacity bin printer. + /// + LargeCapacity = SafeNativeMethods.DMBIN_LARGECAPACITY, + + /// + /// A paper cassette. + /// + Cassette = SafeNativeMethods.DMBIN_CASSETTE, + + FormSource = SafeNativeMethods.DMBIN_FORMSOURCE, + + /// + /// A printer-specific paper source. + /// + Custom = SafeNativeMethods.DMBIN_USER + 1, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPageInfo.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPageInfo.cs new file mode 100644 index 00000000000..85761ecce6a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPageInfo.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies print preview information for a single page. This class cannot be inherited. + /// + public sealed class PreviewPageInfo + { + private readonly Image _image; + + // Physical measures in hundredths of an inch + private Size _physicalSize = Size.Empty; + + /// + /// Initializes a new instance of the class. + /// + public PreviewPageInfo(Image image, Size physicalSize) + { + _image = image; + _physicalSize = physicalSize; + } + + /// + /// Gets the image of the printed page. + /// + public Image Image + { + get { return _image; } + } + + /// + /// Gets the size of the printed page, in hundredths of an inch. + /// + public Size PhysicalSize + { + get { return _physicalSize; } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs new file mode 100644 index 00000000000..e5c135914b8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PreviewPrintController.cs @@ -0,0 +1,137 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Drawing.Text; +using System.Runtime.InteropServices; + +namespace System.Drawing.Printing +{ + /// + /// A PrintController which "prints" to a series of images. + /// + public class PreviewPrintController : PrintController + { + private Graphics? _graphics; + private DeviceContext? _dc; + private readonly ArrayList _list = new ArrayList(); + + public override bool IsPreview => true; + + public virtual bool UseAntiAlias { get; set; } + + public PreviewPageInfo[] GetPreviewPageInfo() + { + var temp = new PreviewPageInfo[_list.Count]; + _list.CopyTo(temp, 0); + return temp; + } + + /// + /// Implements StartPrint for generating print preview information. + /// + public override void OnStartPrint(PrintDocument document, PrintEventArgs e) + { + base.OnStartPrint(document, e); + + if (!document.PrinterSettings.IsValid) + { + throw new InvalidPrinterException(document.PrinterSettings); + } + + // We need a DC as a reference; we don't actually draw on it. + // We make sure to reuse the same one to improve performance. + _dc = document.PrinterSettings.CreateInformationContext(_modeHandle!); + } + + /// + /// Implements StartEnd for generating print preview information. + /// + public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) + { + base.OnStartPage(document, e); + + if (e.CopySettingsToDevMode) + { + e.PageSettings.CopyToHdevmode(_modeHandle!); + } + + Size size = e.PageBounds.Size; + + // Metafile framing rectangles apparently use hundredths of mm as their unit of measurement, + // instead of the GDI+ standard hundredth of an inch. + Size metafileSize = PrinterUnitConvert.Convert(size, PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter); + + // Create a Metafile which accepts only GDI+ commands since we are the ones creating + // and using this ... + // Framework creates a dual-mode EMF for each page in the preview. + // When these images are displayed in preview, + // they are added to the dual-mode EMF. However, + // GDI+ breaks during this process if the image + // is sufficiently large and has more than 254 colors. + // This code path can easily be avoided by requesting + // an EmfPlusOnly EMF.. + Metafile metafile = new Metafile(_dc!.Hdc, new Rectangle(0, 0, metafileSize.Width, metafileSize.Height), MetafileFrameUnit.GdiCompatible, EmfType.EmfPlusOnly); + + PreviewPageInfo info = new PreviewPageInfo(metafile, size); + _list.Add(info); + PrintPreviewGraphics printGraphics = new PrintPreviewGraphics(document, e); + _graphics = Graphics.FromImage(metafile); + + if (document.OriginAtMargins) + { + // Adjust the origin of the graphics object to be at the + // user-specified margin location + int dpiX = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSX); + int dpiY = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSY); + int hardMarginX_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX); + int hardMarginY_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(_dc, _dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY); + float hardMarginX = hardMarginX_DU * 100f / dpiX; + float hardMarginY = hardMarginY_DU * 100f / dpiY; + + _graphics.TranslateTransform(-hardMarginX, -hardMarginY); + _graphics.TranslateTransform(document.DefaultPageSettings.Margins.Left, document.DefaultPageSettings.Margins.Top); + } + + _graphics.PrintingHelper = printGraphics; + + if (UseAntiAlias) + { + _graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + _graphics.SmoothingMode = SmoothingMode.AntiAlias; + } + return _graphics; + } + + /// + /// Implements EndPage for generating print preview information. + /// + public override void OnEndPage(PrintDocument document, PrintPageEventArgs e) + { + if (_graphics != null) + { + _graphics.Dispose(); + _graphics = null; + } + + base.OnEndPage(document, e); + } + + /// + /// Implements EndPrint for generating print preview information. + /// + public override void OnEndPrint(PrintDocument document, PrintEventArgs e) + { + if (_dc != null) + { + _dc.Dispose(); + _dc = null; + } + + base.OnEndPrint(document, e); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintAction.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintAction.cs new file mode 100644 index 00000000000..2ff2d398303 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintAction.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies the type of action for the . + /// + public enum PrintAction + { + /// + /// Printing to a file. + /// + PrintToFile, + /// + /// Printing to a preview. + /// + PrintToPreview, + /// + /// Printing to a printer. + /// + PrintToPrinter + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs new file mode 100644 index 00000000000..2fe1e955f12 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintController.cs @@ -0,0 +1,280 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Drawing.Printing +{ + /// + /// Controls how a document is printed. + /// + public abstract class PrintController + { + /// + /// Represents a SafeHandle for a Printer's Device Mode struct handle (DEVMODE) + /// + /// + /// DEVMODEs are pretty expensive, so we cache one here and share it + /// with the Standard and Preview print controllers. + /// + internal sealed class SafeDeviceModeHandle : SafeHandle + { + public SafeDeviceModeHandle() : base(IntPtr.Zero, ownsHandle: true) + { + } + + internal SafeDeviceModeHandle(IntPtr handle) : base(IntPtr.Zero, ownsHandle: true) + { + SetHandle(handle); + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + /// + /// Specifies how to free the handle. + /// The boolean returned should be true for success and false if the runtime + /// should fire a SafeHandleCriticalFailure MDA (CustomerDebugProbe) if that + /// MDA is enabled. + /// + protected override bool ReleaseHandle() + { + if (!IsInvalid) + { + Interop.Kernel32.GlobalFree(new HandleRef(this, handle)); + } + + handle = IntPtr.Zero; + return true; + } + + public static implicit operator IntPtr(SafeDeviceModeHandle handle) + { + return (handle == null) ? IntPtr.Zero : handle.handle; + } + + public static explicit operator SafeDeviceModeHandle(IntPtr handle) + { + return new SafeDeviceModeHandle(handle); + } + } + + private protected SafeDeviceModeHandle? _modeHandle; + + protected PrintController() + { + } + + public virtual bool IsPreview => false; + + /// + /// When overridden in a derived class, begins the control sequence of when and how to print a page in a document. + /// + public virtual Graphics? OnStartPage(PrintDocument document, PrintPageEventArgs e) + { + return null; + } + + /// + /// When overridden in a derived class, completes the control sequence of when and how to print a page in a document. + /// + public virtual void OnEndPage(PrintDocument document, PrintPageEventArgs e) + { + } + + /// + /// If you have nested PrintControllers, this method won't get called on the inner one. + /// Add initialization code to StartPrint or StartPage instead. + /// + internal void Print(PrintDocument document) + { + // Get the PrintAction for this event + PrintAction printAction; + if (IsPreview) + { + printAction = PrintAction.PrintToPreview; + } + else + { + printAction = document.PrinterSettings.PrintToFile ? PrintAction.PrintToFile : PrintAction.PrintToPrinter; + } + + // Check that user has permission to print to this particular printer + PrintEventArgs printEvent = new PrintEventArgs(printAction); + document.OnBeginPrint(printEvent); + if (printEvent.Cancel) + { + document.OnEndPrint(printEvent); + return; + } + + OnStartPrint(document, printEvent); + if (printEvent.Cancel) + { + document.OnEndPrint(printEvent); + OnEndPrint(document, printEvent); + return; + } + + bool canceled = true; + + try + { + // To enable optimization of the preview dialog, add the following to the config file: + // + // + // + // + canceled = LocalAppContextSwitches.OptimizePrintPreview ? PrintLoopOptimized(document) : PrintLoop(document); + } + finally + { + try + { + document.OnEndPrint(printEvent); + printEvent.Cancel = canceled | printEvent.Cancel; + } + finally + { + OnEndPrint(document, printEvent); + } + } + } + + /// + /// Returns true if print was aborted. + /// + /// + /// If you have nested PrintControllers, this method won't get called on the inner one + /// Add initialization code to StartPrint or StartPage instead. + /// + private bool PrintLoop(PrintDocument document) + { + QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs((PageSettings)document.DefaultPageSettings.Clone()); + while (true) + { + document.OnQueryPageSettings(queryEvent); + if (queryEvent.Cancel) + { + return true; + } + + PrintPageEventArgs pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); + Graphics? graphics = OnStartPage(document, pageEvent); + pageEvent.SetGraphics(graphics); + + try + { + document.OnPrintPage(pageEvent); + OnEndPage(document, pageEvent); + } + finally + { + pageEvent.Dispose(); + } + + if (pageEvent.Cancel) + { + return true; + } + else if (!pageEvent.HasMorePages) + { + return false; + } + } + } + + private bool PrintLoopOptimized(PrintDocument document) + { + PrintPageEventArgs? pageEvent = null; + PageSettings documentPageSettings = (PageSettings)document.DefaultPageSettings.Clone(); + QueryPageSettingsEventArgs queryEvent = new QueryPageSettingsEventArgs(documentPageSettings); + while (true) + { + queryEvent.PageSettingsChanged = false; + document.OnQueryPageSettings(queryEvent); + if (queryEvent.Cancel) + { + return true; + } + + if (!queryEvent.PageSettingsChanged) + { + // QueryPageSettings event handler did not change the page settings, + // thus we use default page settings from the document object. + if (pageEvent == null) + { + pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); + } + else + { + // This is not the first page and the settings had not changed since the previous page, + // thus don't re-apply them. + pageEvent.CopySettingsToDevMode = false; + } + + Graphics? graphics = OnStartPage(document, pageEvent); + pageEvent.SetGraphics(graphics); + } + else + { + // Page settings were customized, so use the customized ones in the start page event. + pageEvent = CreatePrintPageEvent(queryEvent.PageSettings); + Graphics? graphics = OnStartPage(document, pageEvent); + pageEvent.SetGraphics(graphics); + } + + try + { + document.OnPrintPage(pageEvent); + OnEndPage(document, pageEvent); + } + finally + { + pageEvent.Graphics!.Dispose(); + pageEvent.SetGraphics(null); + } + + if (pageEvent.Cancel) + { + return true; + } + else if (!pageEvent.HasMorePages) + { + return false; + } + } + } + + private PrintPageEventArgs CreatePrintPageEvent(PageSettings pageSettings) + { + Debug.Assert((_modeHandle != null), "modeHandle is null. Someone must have forgot to call base.StartPrint"); + + + Rectangle pageBounds = pageSettings.GetBounds(_modeHandle); + Rectangle marginBounds = new Rectangle(pageSettings.Margins.Left, + pageSettings.Margins.Top, + pageBounds.Width - (pageSettings.Margins.Left + pageSettings.Margins.Right), + pageBounds.Height - (pageSettings.Margins.Top + pageSettings.Margins.Bottom)); + + PrintPageEventArgs pageEvent = new PrintPageEventArgs(null, marginBounds, pageBounds, pageSettings); + return pageEvent; + } + + /// + /// When overridden in a derived class, begins the control sequence of when and how to print a document. + /// + public virtual void OnStartPrint(PrintDocument document, PrintEventArgs e) + { + _modeHandle = (SafeDeviceModeHandle)document.PrinterSettings.GetHdevmode(document.DefaultPageSettings); + } + + /// + /// When overridden in a derived class, completes the control sequence of when and how to print a document. + /// + public virtual void OnEndPrint(PrintDocument document, PrintEventArgs e) + { + _modeHandle?.Close(); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.cs new file mode 100644 index 00000000000..c315870ec68 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintDocument.cs @@ -0,0 +1,234 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace System.Drawing.Printing +{ + /// + /// Defines a reusable object that sends output to the printer. + /// + [DefaultProperty("DocumentName"), DefaultEvent("PrintPage"), SRDescription(nameof(SR.PrintDocumentDesc))] + public class PrintDocument : Component + { + private string _documentName = "document"; + + private PrintEventHandler? _beginPrintHandler; + private PrintEventHandler? _endPrintHandler; + private PrintPageEventHandler? _printPageHandler; + private QueryPageSettingsEventHandler? _queryHandler; + + private PrinterSettings _printerSettings = new PrinterSettings(); + private PageSettings _defaultPageSettings; + + private PrintController? _printController; + + private bool _originAtMargins; + private bool _userSetPageSettings; + + /// + /// Initializes a new instance of the class. + /// + public PrintDocument() + { + _defaultPageSettings = new PageSettings(_printerSettings); + } + + /// + /// Gets or sets the default page settings for the document being printed. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(nameof(SR.PDOCdocumentPageSettingsDescr)) + ] + public PageSettings DefaultPageSettings + { + get { return _defaultPageSettings; } + set + { + _defaultPageSettings = value ?? new PageSettings(); + _userSetPageSettings = true; + } + } + + /// + /// Gets or sets the name to display to the user while printing the document; for example, in a print status + /// dialog or a printer queue. + /// + [ + DefaultValue("document"), + SRDescription(nameof(SR.PDOCdocumentNameDescr)) + ] + public string DocumentName + { + get => _documentName; + set => _documentName = value ?? ""; + } + + // If true, positions the origin of the graphics object + // associated with the page at the point just inside + // the user-specified margins of the page. + // If false, the graphics origin is at the top-left + // corner of the printable area of the page. + [ + DefaultValue(false), + SRDescription(nameof(SR.PDOCoriginAtMarginsDescr)) + ] + public bool OriginAtMargins + { + get + { + return _originAtMargins; + } + set + { + _originAtMargins = value; + } + } + + /// + /// Gets or sets the that guides the printing process. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(nameof(SR.PDOCprintControllerDescr)) + ] + public PrintController PrintController + { + get => _printController ??= new StandardPrintController(); + set => _printController = value; + } + + /// + /// Gets or sets the printer on which the document is printed. + /// + [ + Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), + SRDescription(nameof(SR.PDOCprinterSettingsDescr)) + ] + public PrinterSettings PrinterSettings + { + get { return _printerSettings; } + set + { + value ??= new PrinterSettings(); + _printerSettings = value; + // reset the PageSettings that match the PrinterSettings only if we have created the defaultPageSettings.. + if (!_userSetPageSettings) + { + _defaultPageSettings = _printerSettings.DefaultPageSettings; + } + } + } + + /// + /// Occurs when the method is called, before the first page prints. + /// + [SRDescription(nameof(SR.PDOCbeginPrintDescr))] + public event PrintEventHandler BeginPrint + { + add + { + _beginPrintHandler += value; + } + remove + { + _beginPrintHandler -= value; + } + } + + /// + /// Occurs when is called, after the last page is printed. + /// + [SRDescription(nameof(SR.PDOCendPrintDescr))] + public event PrintEventHandler EndPrint + { + add + { + _endPrintHandler += value; + } + remove + { + _endPrintHandler -= value; + } + } + + /// + /// Occurs when a page is printed. + /// + [SRDescription(nameof(SR.PDOCprintPageDescr))] + public event PrintPageEventHandler PrintPage + { + add + { + _printPageHandler += value; + } + remove + { + _printPageHandler -= value; + } + } + + [SRDescription(nameof(SR.PDOCqueryPageSettingsDescr))] + public event QueryPageSettingsEventHandler QueryPageSettings + { + add + { + _queryHandler += value; + } + remove + { + _queryHandler -= value; + } + } + + /// + /// Raises the event. + /// + protected internal virtual void OnBeginPrint(PrintEventArgs e) + { + _beginPrintHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + protected internal virtual void OnEndPrint(PrintEventArgs e) + { + _endPrintHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + protected internal virtual void OnPrintPage(PrintPageEventArgs e) + { + _printPageHandler?.Invoke(this, e); + } + + /// + /// Raises the event. + /// + protected internal virtual void OnQueryPageSettings(QueryPageSettingsEventArgs e) + { + _queryHandler?.Invoke(this, e); + } + + /// + /// Prints the document. + /// + public void Print() + { + PrintController controller = PrintController; + controller.Print(this); + } + + /// + /// Provides some interesting information about the PrintDocument in String form. + /// + public override string ToString() => $"[PrintDocument {DocumentName}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventArgs.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventArgs.cs new file mode 100644 index 00000000000..1abd0698fdd --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventArgs.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; + +namespace System.Drawing.Printing +{ + /// + /// Provides data for the and events. + /// + public class PrintEventArgs : CancelEventArgs + { + private readonly PrintAction _printAction; + + /// + /// Initializes a new instance of the class. + /// + public PrintEventArgs() + { + } + + /// + /// Initializes a new instance of the class. + /// + internal PrintEventArgs(PrintAction action) + { + _printAction = action; + } + + /// + /// Specifies which is causing this event. + /// + public PrintAction PrintAction + { + get + { + return _printAction; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventHandler.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventHandler.cs new file mode 100644 index 00000000000..45a5f3f0f25 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintEventHandler.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Represents the method that will handle the , + /// , or + /// event of a . + /// + public delegate void PrintEventHandler(object sender, PrintEventArgs e); +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventArgs.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventArgs.cs new file mode 100644 index 00000000000..7c79df00297 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventArgs.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Provides data for the event. + /// + // NOTE: Please keep this class consistent with PaintEventArgs. + public class PrintPageEventArgs : EventArgs + { + private bool _hasMorePages; + private bool _cancel; + + private Graphics? _graphics; + private readonly Rectangle _marginBounds; + private readonly Rectangle _pageBounds; + private readonly PageSettings _pageSettings; + + // Apply page settings to the printer. + internal bool CopySettingsToDevMode = true; + + + /// + /// Initializes a new instance of the class. + /// + public PrintPageEventArgs(Graphics? graphics, Rectangle marginBounds, Rectangle pageBounds, PageSettings pageSettings) + { + _graphics = graphics; // may be null, see PrintController + _marginBounds = marginBounds; + _pageBounds = pageBounds; + _pageSettings = pageSettings; + } + + /// + /// Gets or sets a value indicating whether the print job should be canceled. + /// + public bool Cancel + { + get { return _cancel; } + set { _cancel = value; } + } + + /// + /// Gets the used to paint the item. + /// + public Graphics? Graphics + { + get + { + return _graphics; + } + } + + /// + /// Gets or sets a value indicating whether an additional page should be printed. + /// + public bool HasMorePages + { + get { return _hasMorePages; } + set { _hasMorePages = value; } + } + + /// + /// Gets the rectangular area that represents the portion of the page between the margins. + /// + public Rectangle MarginBounds + { + get + { + return _marginBounds; + } + } + + /// + /// Gets the rectangular area that represents the total area of the page. + /// + public Rectangle PageBounds + { + get + { + return _pageBounds; + } + } + + /// + /// Gets the page settings for the current page. + /// + public PageSettings PageSettings + { + get + { + return _pageSettings; + } + } + + /// + /// Disposes of the resources (other than memory) used by the . + /// + internal void Dispose() + { + _graphics!.Dispose(); + } + + internal void SetGraphics(Graphics? value) + { + _graphics = value; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventHandler.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventHandler.cs new file mode 100644 index 00000000000..8836bb59754 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPageEventHandler.cs @@ -0,0 +1,10 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Represents the method that will handle the event of a . + /// + public delegate void PrintPageEventHandler(object sender, PrintPageEventArgs e); +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs new file mode 100644 index 00000000000..f4907591b8d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintPreviewGraphics.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Internal; +using System.Drawing.Printing; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// Retrieves the printer graphics during preview. + /// + internal sealed class PrintPreviewGraphics + { + private readonly PrintPageEventArgs _printPageEventArgs; + private readonly PrintDocument _printDocument; + + public PrintPreviewGraphics(PrintDocument document, PrintPageEventArgs e) + { + _printPageEventArgs = e; + _printDocument = document; + } + + /// + /// Gets the Visible bounds of this graphics object. Used during print preview. + /// + public RectangleF VisibleClipBounds + { + get + { + IntPtr hdevMode = _printPageEventArgs.PageSettings.PrinterSettings.GetHdevmodeInternal(); + + using (DeviceContext dc = _printPageEventArgs.PageSettings.PrinterSettings.CreateDeviceContext(hdevMode)) + { + using (Graphics graphics = Graphics.FromHdcInternal(dc.Hdc)) + { + if (_printDocument.OriginAtMargins) + { + // Adjust the origin of the graphics object to be at the user-specified margin location + // Note: Graphics.FromHdc internally calls SaveDC(hdc), we can still use the saved hdc to get the resolution. + int dpiX = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSX); + int dpiY = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.LOGPIXELSY); + int hardMarginX_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETX); + int hardMarginY_DU = Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), Interop.Gdi32.DeviceCapability.PHYSICALOFFSETY); + float hardMarginX = hardMarginX_DU * 100 / dpiX; + float hardMarginY = hardMarginY_DU * 100 / dpiY; + + graphics.TranslateTransform(-hardMarginX, -hardMarginY); + graphics.TranslateTransform(_printDocument.DefaultPageSettings.Margins.Left, _printDocument.DefaultPageSettings.Margins.Top); + } + + return graphics.VisibleClipBounds; + } + } + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrintRange.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintRange.cs new file mode 100644 index 00000000000..a1f3f6576c9 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrintRange.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies the option buttons in the print dialog box that designate the part of the document to print. + /// + public enum PrintRange + { + /// + /// All pages are printed. + /// + AllPages = SafeNativeMethods.PD_ALLPAGES, + + /// + /// The pages between and are printed. + /// + SomePages = SafeNativeMethods.PD_PAGENUMS, + + /// + /// The selected pages are printed. + /// + Selection = SafeNativeMethods.PD_SELECTION, + + /// + /// The current page is printed. The print dialog box requires Windows 2000 or later for this setting; if used + /// with an earlier operating system, all pages will be printed. + /// + CurrentPage = SafeNativeMethods.PD_CURRENTPAGE, + + // When adding new members, be sure to update PrintDialog.printRangeMask. + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.Serializable.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.Serializable.cs new file mode 100644 index 00000000000..6cabac964b7 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.Serializable.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// This file isn't built into the .csproj in the runtime libraries but is consumed by Mono. + +namespace System.Drawing.Printing +{ + [Serializable] + partial class PrinterResolution + { + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.cs new file mode 100644 index 00000000000..20b0091cfe8 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolution.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Globalization; + +namespace System.Drawing.Printing +{ + /// + /// Retrieves the resolution supported by a printer. + /// + public partial class PrinterResolution + { + private int _x; + private int _y; + private PrinterResolutionKind _kind; + + /// + /// Initializes a new instance of the class with default properties. + /// + public PrinterResolution() + { + _kind = PrinterResolutionKind.Custom; + } + + internal PrinterResolution(PrinterResolutionKind kind, int x, int y) + { + _kind = kind; + _x = x; + _y = y; + } + + /// + /// Gets a value indicating the kind of printer resolution. + /// + public PrinterResolutionKind Kind + { + get => _kind; + set + { + if (value < PrinterResolutionKind.High || value > PrinterResolutionKind.Custom) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PrinterResolutionKind)); + } + + _kind = value; + } + } + + /// + /// Gets the printer resolution in the horizontal direction, in dots per inch. + /// + public int X + { + get => _x; + set => _x = value; + } + + /// + /// Gets the printer resolution in the vertical direction, in dots per inch. + /// + public int Y + { + get => _y; + set => _y = value; + } + + /// + /// Provides some interesting information about the PrinterResolution in String form. + /// + public override string ToString() + { + if (_kind != PrinterResolutionKind.Custom) + { + return $"[PrinterResolution {Kind}]"; + } + + + return FormattableString.Invariant($"[PrinterResolution X={X} Y={Y}]"); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolutionKind.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolutionKind.cs new file mode 100644 index 00000000000..19ecb4247a7 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterResolutionKind.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies a printer resolution. + /// + public enum PrinterResolutionKind + { + /// + /// High resolution. + /// + High = SafeNativeMethods.DMRES_HIGH, + /// + /// Medium resolution. + /// + Medium = SafeNativeMethods.DMRES_MEDIUM, + /// + /// Low resolution. + /// + Low = SafeNativeMethods.DMRES_LOW, + /// + /// Draft-quality resolution. + /// + Draft = SafeNativeMethods.DMRES_DRAFT, + /// + /// Custom resolution. + /// + Custom = 0, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.cs new file mode 100644 index 00000000000..441809b55b1 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterSettings.cs @@ -0,0 +1,1654 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.Drawing.Internal; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; + +namespace System.Drawing.Printing +{ + /// + /// Information about how a document should be printed, including which printer to print it on. + /// + public class PrinterSettings : ICloneable + { + // All read/write data is stored in managed code, and whenever we need to call Win32, + // we create new DEVMODE and DEVNAMES structures. We don't store device capabilities, + // though. + // + // Also, all properties have hidden tri-state logic -- yes/no/default + private const int Padding64Bit = 4; + + private string? _printerName; // default printer. + private string _driverName = ""; + private string _outputPort = ""; + private bool _printToFile; + + // Whether the PrintDialog has been shown (not whether it's currently shown). This is how we enforce SafePrinting. + private bool _printDialogDisplayed; + + private short _extrabytes; + private byte[]? _extrainfo; + + private short _copies = -1; + private Duplex _duplex = System.Drawing.Printing.Duplex.Default; + private TriState _collate = TriState.Default; + private readonly PageSettings _defaultPageSettings; + private int _fromPage; + private int _toPage; + private int _maxPage = 9999; + private int _minPage; + private PrintRange _printRange; + + private short _devmodebytes; + private byte[]? _cachedDevmode; + + /// + /// Initializes a new instance of the class. + /// + public PrinterSettings() + { + _defaultPageSettings = new PageSettings(this); + } + + /// + /// Gets a value indicating whether the printer supports duplex (double-sided) printing. + /// + public bool CanDuplex + { + get { return DeviceCapabilities(SafeNativeMethods.DC_DUPLEX, IntPtr.Zero, 0) == 1; } + } + + /// + /// Gets or sets the number of copies to print. + /// + public short Copies + { + get + { + if (_copies != -1) + return _copies; + else + return GetModeField(ModeField.Copies, 1); + } + set + { + if (value < 0) + throw new ArgumentException(SR.Format(SR.InvalidLowBoundArgumentEx, + nameof(value), value.ToString(CultureInfo.CurrentCulture), + (0).ToString(CultureInfo.CurrentCulture))); + /* + We shouldnt allow copies to be set since the copies can be a large number + and can be reflected in PrintDialog. So for the Copies property, + we prefer that for SafePrinting, copied cannot be set programmatically + but through the print dialog. + Any lower security could set copies to anything. + */ + _copies = value; + } + } + + /// + /// Gets or sets a value indicating whether the print out is collated. + /// + public bool Collate + { + get + { + if (!_collate.IsDefault) + return (bool)_collate; + else + return GetModeField(ModeField.Collate, SafeNativeMethods.DMCOLLATE_FALSE) == SafeNativeMethods.DMCOLLATE_TRUE; + } + set { _collate = value; } + } + + /// + /// Gets the default page settings for this printer. + /// + public PageSettings DefaultPageSettings + { + get { return _defaultPageSettings; } + } + + // As far as I can tell, Windows no longer pays attention to driver names and output ports. + // But I'm leaving this code in place in case I'm wrong. + internal string DriverName + { + get { return _driverName; } + } + + /// + /// Gets or sets the printer's duplex setting. + /// + public Duplex Duplex + { + get + { + if (_duplex != Duplex.Default) + { + return _duplex; + } + + return (Duplex)GetModeField(ModeField.Duplex, SafeNativeMethods.DMDUP_SIMPLEX); + } + set + { + if (value < Duplex.Default || value > Duplex.Horizontal) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(Duplex)); + } + + _duplex = value; + } + } + + /// + /// Gets or sets the first page to print. + /// + public int FromPage + { + get { return _fromPage; } + set + { + if (value < 0) + throw new ArgumentException(SR.Format(SR.InvalidLowBoundArgumentEx, + nameof(value), value.ToString(CultureInfo.CurrentCulture), + (0).ToString(CultureInfo.CurrentCulture))); + _fromPage = value; + } + } + + + + /// + /// Gets the names of all printers installed on the machine. + /// + public static unsafe StringCollection InstalledPrinters + { + get + { + int sizeofstruct; + // Note: The call to get the size of the buffer required for level 5 does not work properly on NT platforms. + const int Level = 4; + // PRINTER_INFO_4 is 12 or 24 bytes in size depending on the architecture. + if (IntPtr.Size == 8) + { + sizeofstruct = (IntPtr.Size * 2) + (sizeof(int) * 1) + Padding64Bit; + } + else + { + sizeofstruct = (IntPtr.Size * 2) + (sizeof(int) * 1); + } + + int bufferSize; + int count; + Interop.Winspool.EnumPrinters(SafeNativeMethods.PRINTER_ENUM_LOCAL | SafeNativeMethods.PRINTER_ENUM_CONNECTIONS, null, Level, IntPtr.Zero, 0, out bufferSize, out _); + + IntPtr buffer = Marshal.AllocCoTaskMem(bufferSize); + int returnCode = Interop.Winspool.EnumPrinters(SafeNativeMethods.PRINTER_ENUM_LOCAL | SafeNativeMethods.PRINTER_ENUM_CONNECTIONS, + null, Level, buffer, + bufferSize, out _, out count); + var array = new string[count]; + + if (returnCode == 0) + { + Marshal.FreeCoTaskMem(buffer); + throw new Win32Exception(); + } + + byte* pBuffer = (byte*)buffer; + for (int i = 0; i < count; i++) + { + // The printer name is at offset 0 + IntPtr namePointer = *(IntPtr*)(pBuffer + (nint)i * sizeofstruct); + array[i] = Marshal.PtrToStringAuto(namePointer)!; + } + + Marshal.FreeCoTaskMem(buffer); + + return new StringCollection(array); + } + } + + /// + /// Gets a value indicating whether the property designates the default printer. + /// + public bool IsDefaultPrinter + { + get + { + return (_printerName == null || _printerName == GetDefaultPrinterName()); + } + } + + /// + /// Gets a value indicating whether the printer is a plotter, as opposed to a raster printer. + /// + public bool IsPlotter + { + get + { + return GetDeviceCaps(Interop.Gdi32.DeviceCapability.TECHNOLOGY) == Interop.Gdi32.DeviceTechnology.DT_PLOTTER; + } + } + + /// + /// Gets a value indicating whether the property designates a valid printer. + /// + public bool IsValid + { + get + { + return DeviceCapabilities(SafeNativeMethods.DC_COPIES, IntPtr.Zero, -1) != -1; + } + } + + /// + /// Gets the angle, in degrees, which the portrait orientation is rotated to produce the landscape orientation. + /// + public int LandscapeAngle + { + get { return DeviceCapabilities(SafeNativeMethods.DC_ORIENTATION, IntPtr.Zero, 0); } + } + + /// + /// Gets the maximum number of copies allowed by the printer. + /// + public int MaximumCopies + { + get { return DeviceCapabilities(SafeNativeMethods.DC_COPIES, IntPtr.Zero, 1); } + } + + /// + /// Gets or sets the highest or which may be selected in a print dialog box. + /// + public int MaximumPage + { + get { return _maxPage; } + set + { + if (value < 0) + throw new ArgumentException(SR.Format(SR.InvalidLowBoundArgumentEx, + nameof(value), value.ToString(CultureInfo.CurrentCulture), + (0).ToString(CultureInfo.CurrentCulture))); + _maxPage = value; + } + } + + /// + /// Gets or sets the lowest or which may be selected in a print dialog box. + /// + public int MinimumPage + { + get { return _minPage; } + set + { + if (value < 0) + throw new ArgumentException(SR.Format(SR.InvalidLowBoundArgumentEx, + nameof(value), value.ToString(CultureInfo.CurrentCulture), + (0).ToString(CultureInfo.CurrentCulture))); + _minPage = value; + } + } + + internal string OutputPort + { + get + { + return _outputPort; + } + set + { + _outputPort = value; + } + } + + /// + /// Indicates the name of the printerfile. + /// + public string PrintFileName + { + get + { + string printFileName = OutputPort; + return printFileName; + } + set + { + if (string.IsNullOrEmpty(value)) + { + throw new ArgumentNullException(value); + } + OutputPort = value; + } + } + + /// + /// Gets the paper sizes supported by this printer. + /// + public PaperSizeCollection PaperSizes + { + get { return new PaperSizeCollection(Get_PaperSizes()); } + } + + /// + /// Gets the paper sources available on this printer. + /// + public PaperSourceCollection PaperSources + { + get { return new PaperSourceCollection(Get_PaperSources()); } + } + + /// + /// Whether the print dialog has been displayed. In SafePrinting mode, a print dialog is required to print. + /// After printing, this property is set to false if the program does not have AllPrinting; this guarantees + /// a document is only printed once each time the print dialog is shown. + /// + internal bool PrintDialogDisplayed + { + get + { + return _printDialogDisplayed; + } + + set + { + _printDialogDisplayed = value; + } + } + + /// + /// Gets or sets the pages the user has asked to print. + /// + public PrintRange PrintRange + { + get { return _printRange; } + set + { + if (!Enum.IsDefined(value)) + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(PrintRange)); + + _printRange = value; + } + } + + /// + /// Indicates whether to print to a file instead of a port. + /// + public bool PrintToFile + { + get + { + return _printToFile; + } + set + { + _printToFile = value; + } + } + + /// + /// Gets or sets the name of the printer. + /// + public string PrinterName + { + get + { + return PrinterNameInternal; + } + + set + { + PrinterNameInternal = value; + } + } + + private string PrinterNameInternal + { + get + { + if (_printerName == null) + return GetDefaultPrinterName(); + else + return _printerName; + } + set + { + // Reset the DevMode and Extrabytes... + _cachedDevmode = null; + _extrainfo = null; + _printerName = value; + // PrinterName can be set through a fulltrusted assembly without using the PrintDialog. + // So dont set this variable here. + //PrintDialogDisplayed = true; + } + } + + /// + /// Gets the resolutions supported by this printer. + /// + public PrinterResolutionCollection PrinterResolutions + { + get { return new PrinterResolutionCollection(Get_PrinterResolutions()); } + } + + /// + /// If the image is a JPEG or a PNG (Image.RawFormat) and the printer returns true from + /// ExtEscape(CHECKJPEGFORMAT) or ExtEscape(CHECKPNGFORMAT) then this function returns true. + /// + public bool IsDirectPrintingSupported(ImageFormat imageFormat) + { + bool isDirectPrintingSupported = false; + if (imageFormat.Equals(ImageFormat.Jpeg) || imageFormat.Equals(ImageFormat.Png)) + { + int nEscape = imageFormat.Equals(ImageFormat.Jpeg) ? Interop.Gdi32.CHECKJPEGFORMAT : Interop.Gdi32.CHECKPNGFORMAT; + int outData; + DeviceContext dc = CreateInformationContext(DefaultPageSettings); + HandleRef hdc = new HandleRef(dc, dc.Hdc); + try + { + isDirectPrintingSupported = Interop.Gdi32.ExtEscape(hdc, Interop.Gdi32.QUERYESCSUPPORT, sizeof(int), ref nEscape, 0, out outData) > 0; + } + finally + { + dc.Dispose(); + } + } + return isDirectPrintingSupported; + } + + /// + /// This method utilizes the CHECKJPEGFORMAT/CHECKPNGFORMAT printer escape functions + /// to determine whether the printer can handle a JPEG image. + /// + /// If the image is a JPEG or a PNG (Image.RawFormat) and the printer returns true + /// from ExtEscape(CHECKJPEGFORMAT) or ExtEscape(CHECKPNGFORMAT) then this function returns true. + /// + public bool IsDirectPrintingSupported(Image image) + { + bool isDirectPrintingSupported = false; + if (image.RawFormat.Equals(ImageFormat.Jpeg) || image.RawFormat.Equals(ImageFormat.Png)) + { + MemoryStream stream = new MemoryStream(); + try + { + image.Save(stream, image.RawFormat); + + byte[] pvImage = stream.ToArray(); + + int nEscape = image.RawFormat.Equals(ImageFormat.Jpeg) ? Interop.Gdi32.CHECKJPEGFORMAT : Interop.Gdi32.CHECKPNGFORMAT; + int outData = 0; + + DeviceContext dc = CreateInformationContext(DefaultPageSettings); + HandleRef hdc = new HandleRef(dc, dc.Hdc); + try + { + bool querySupported = Interop.Gdi32.ExtEscape(hdc, Interop.Gdi32.QUERYESCSUPPORT, sizeof(int), ref nEscape, 0, out outData) > 0; + if (querySupported) + { + isDirectPrintingSupported = (Interop.Gdi32.ExtEscape(hdc, nEscape, pvImage.Length, pvImage, sizeof(int), out outData) > 0) + && (outData == 1); + } + } + finally + { + dc.Dispose(); + } + } + finally + { + stream.Close(); + } + } + return isDirectPrintingSupported; + } + + /// + /// Gets a value indicating whether the printer supports color printing. + /// + public bool SupportsColor + { + get + { + // If the printer supports color printing, the return value is 1; otherwise, the return value is zero. + // The pointerToBuffer parameter is not used. + return DeviceCapabilities( + capability: SafeNativeMethods.DC_COLORDEVICE, + pointerToBuffer: IntPtr.Zero, + defaultValue: 0) == 1; + } + } + + /// + /// Gets or sets the last page to print. + /// + public int ToPage + { + get { return _toPage; } + set + { + if (value < 0) + throw new ArgumentException(SR.Format(SR.InvalidLowBoundArgumentEx, + nameof(value), value.ToString(CultureInfo.CurrentCulture), + (0).ToString(CultureInfo.CurrentCulture))); + _toPage = value; + } + } + + /// + /// Creates an identical copy of this object. + /// + public object Clone() + { + PrinterSettings clone = (PrinterSettings)MemberwiseClone(); + clone._printDialogDisplayed = false; + return clone; + } + // what is done in copytohdevmode cannot give unwanted access AllPrinting permission + internal DeviceContext CreateDeviceContext(PageSettings pageSettings) + { + IntPtr modeHandle = GetHdevmodeInternal(); + DeviceContext? dc = null; + + try + { + //Copy the PageSettings to the DEVMODE... + pageSettings.CopyToHdevmode(modeHandle); + dc = CreateDeviceContext(modeHandle); + } + finally + { + Interop.Kernel32.GlobalFree(modeHandle); + } + return dc; + } + + internal DeviceContext CreateDeviceContext(IntPtr hdevmode) + { + IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode); + DeviceContext dc = DeviceContext.CreateDC(DriverName, PrinterNameInternal, fileName:null, modePointer); + Interop.Kernel32.GlobalUnlock(hdevmode); + return dc; + } + + // A read-only DC, which is faster than CreateHdc + // what is done in copytohdevmode cannot give unwanted access AllPrinting permission + internal DeviceContext CreateInformationContext(PageSettings pageSettings) + { + IntPtr modeHandle = GetHdevmodeInternal(); + DeviceContext dc; + + try + { + //Copy the PageSettings to the DEVMODE... + pageSettings.CopyToHdevmode(modeHandle); + dc = CreateInformationContext(modeHandle); + } + finally + { + Interop.Kernel32.GlobalFree(modeHandle); + } + return dc; + } + + // A read-only DC, which is faster than CreateHdc + internal DeviceContext CreateInformationContext(IntPtr hdevmode) + { + IntPtr modePointer = Interop.Kernel32.GlobalLock(hdevmode); + DeviceContext dc = DeviceContext.CreateIC(DriverName, PrinterNameInternal, fileName:null, modePointer); + Interop.Kernel32.GlobalUnlock(hdevmode); + return dc; + } + + public Graphics CreateMeasurementGraphics() + { + return CreateMeasurementGraphics(DefaultPageSettings); + } + + //whatever the call stack calling HardMarginX and HardMarginY here is safe + public Graphics CreateMeasurementGraphics(bool honorOriginAtMargins) + { + Graphics g = CreateMeasurementGraphics(); + if (honorOriginAtMargins) + { + g.TranslateTransform(-_defaultPageSettings.HardMarginX, -_defaultPageSettings.HardMarginY); + g.TranslateTransform(_defaultPageSettings.Margins.Left, _defaultPageSettings.Margins.Top); + } + return g; + } + + public Graphics CreateMeasurementGraphics(PageSettings pageSettings) + { + // returns the Graphics object for the printer + DeviceContext dc = CreateDeviceContext(pageSettings); + Graphics g = Graphics.FromHdcInternal(dc.Hdc); + g.PrintingHelper = dc; // Graphics will dispose of the DeviceContext. + return g; + } + + //whatever the call stack calling HardMarginX and HardMarginY here is safe + public Graphics CreateMeasurementGraphics(PageSettings pageSettings, bool honorOriginAtMargins) + { + Graphics g = CreateMeasurementGraphics(); + if (honorOriginAtMargins) + { + g.TranslateTransform(-pageSettings.HardMarginX, -pageSettings.HardMarginY); + g.TranslateTransform(pageSettings.Margins.Left, pageSettings.Margins.Top); + } + return g; + } + + // Create a PRINTDLG with a few useful defaults. + // Try to keep this consistent with PrintDialog.CreatePRINTDLG. + private static unsafe void CreatePRINTDLGX86(out Interop.Comdlg32.PRINTDLGX86 data) + { + data = default; + data.lStructSize = sizeof(Interop.Comdlg32.PRINTDLGX86); + data.nFromPage = 1; + data.nToPage = 1; + data.nMinPage = 0; + data.nMaxPage = 9999; + data.nCopies = 1; + } + + // Create a PRINTDLG with a few useful defaults. + // Try to keep this consistent with PrintDialog.CreatePRINTDLG. + private static unsafe void CreatePRINTDLG(out Interop.Comdlg32.PRINTDLG data) + { + data = default; + data.lStructSize = sizeof(Interop.Comdlg32.PRINTDLG); + data.nFromPage = 1; + data.nToPage = 1; + data.nMinPage = 0; + data.nMaxPage = 9999; + data.nCopies = 1; + } + + // Use FastDeviceCapabilities where possible -- computing PrinterName is quite slow + private int DeviceCapabilities(short capability, IntPtr pointerToBuffer, int defaultValue) + { + string printerName = PrinterName; + return FastDeviceCapabilities(capability, pointerToBuffer, defaultValue, printerName); + } + + // We pass PrinterName in as a parameter rather than computing it ourselves because it's expensive to compute. + // We need to pass IntPtr.Zero since passing HDevMode is non-performant. + private static int FastDeviceCapabilities(short capability, IntPtr pointerToBuffer, int defaultValue, string printerName) + { + int result = Interop.Winspool.DeviceCapabilities(printerName, GetOutputPort(), + capability, pointerToBuffer, IntPtr.Zero); + if (result == -1) + return defaultValue; + return result; + } + + // Called by get_PrinterName + private static string GetDefaultPrinterName() + { + if (IntPtr.Size == 8) + { + CreatePRINTDLG(out Interop.Comdlg32.PRINTDLG data); + data.Flags = SafeNativeMethods.PD_RETURNDEFAULT; + bool status = Interop.Comdlg32.PrintDlg(ref data); + + if (!status) + return SR.NoDefaultPrinter; + + IntPtr handle = data.hDevNames; + IntPtr names = Interop.Kernel32.GlobalLock(handle); + if (names == IntPtr.Zero) + throw new Win32Exception(); + + string name = ReadOneDEVNAME(names, 1); + Interop.Kernel32.GlobalUnlock(handle); + + // Windows allocates them, but we have to free them + Interop.Kernel32.GlobalFree(data.hDevNames); + Interop.Kernel32.GlobalFree(data.hDevMode); + + return name; + } + else + { + CreatePRINTDLGX86(out Interop.Comdlg32.PRINTDLGX86 data); + data.Flags = SafeNativeMethods.PD_RETURNDEFAULT; + bool status = Interop.Comdlg32.PrintDlg(ref data); + + if (!status) + return SR.NoDefaultPrinter; + + IntPtr handle = data.hDevNames; + IntPtr names = Interop.Kernel32.GlobalLock(handle); + if (names == IntPtr.Zero) + throw new Win32Exception(); + + string name = ReadOneDEVNAME(names, 1); + Interop.Kernel32.GlobalUnlock(handle); + + // Windows allocates them, but we have to free them + Interop.Kernel32.GlobalFree(data.hDevNames); + Interop.Kernel32.GlobalFree(data.hDevMode); + + return name; + } + } + + + // Called by get_OutputPort + private static string GetOutputPort() + { + if (IntPtr.Size == 8) + { + CreatePRINTDLG(out Interop.Comdlg32.PRINTDLG data); + data.Flags = SafeNativeMethods.PD_RETURNDEFAULT; + bool status = Interop.Comdlg32.PrintDlg(ref data); + if (!status) + return SR.NoDefaultPrinter; + + IntPtr handle = data.hDevNames; + IntPtr names = Interop.Kernel32.GlobalLock(handle); + if (names == IntPtr.Zero) + throw new Win32Exception(); + + string name = ReadOneDEVNAME(names, 2); + + Interop.Kernel32.GlobalUnlock(handle); + + // Windows allocates them, but we have to free them + Interop.Kernel32.GlobalFree(data.hDevNames); + Interop.Kernel32.GlobalFree(data.hDevMode); + + return name; + } + else + { + CreatePRINTDLGX86(out Interop.Comdlg32.PRINTDLGX86 data); + data.Flags = SafeNativeMethods.PD_RETURNDEFAULT; + bool status = Interop.Comdlg32.PrintDlg(ref data); + + if (!status) + return SR.NoDefaultPrinter; + + IntPtr handle = data.hDevNames; + IntPtr names = Interop.Kernel32.GlobalLock(handle); + if (names == IntPtr.Zero) + throw new Win32Exception(); + + string name = ReadOneDEVNAME(names, 2); + + Interop.Kernel32.GlobalUnlock(handle); + + // Windows allocates them, but we have to free them + Interop.Kernel32.GlobalFree(data.hDevNames); + Interop.Kernel32.GlobalFree(data.hDevMode); + + return name; + } + } + + private int GetDeviceCaps(Interop.Gdi32.DeviceCapability capability) + { + using (DeviceContext dc = CreateInformationContext(DefaultPageSettings)) + { + return Interop.Gdi32.GetDeviceCaps(new HandleRef(dc, dc.Hdc), capability); + } + } + + /// + /// Creates a handle to a DEVMODE structure which correspond too the printer settings.When you are done with the + /// handle, you must deallocate it yourself: + /// Interop.Kernel32.GlobalFree(handle); + /// Where "handle" is the return value from this method. + /// + public IntPtr GetHdevmode() + { + IntPtr modeHandle = GetHdevmodeInternal(); + _defaultPageSettings.CopyToHdevmode(modeHandle); + return modeHandle; + } + + internal IntPtr GetHdevmodeInternal() + { + // getting the printer name is quite expensive if PrinterName is left default, + // because it needs to figure out what the default printer is + return GetHdevmodeInternal(PrinterNameInternal); + } + + private unsafe IntPtr GetHdevmodeInternal(string printer) + { + // Create DEVMODE + int modeSize = Interop.Winspool.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, IntPtr.Zero, NativeMethods.NullHandleRef, 0); + if (modeSize < 1) + { + throw new InvalidPrinterException(this); + } + IntPtr handle = Interop.Kernel32.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE, (uint)modeSize); // cannot be <0 anyway + IntPtr pointer = Interop.Kernel32.GlobalLock(handle); + + //Get the DevMode only if its not cached.... + if (_cachedDevmode != null) + { + Marshal.Copy(_cachedDevmode, 0, pointer, _devmodebytes); + } + else + { + int returnCode = Interop.Winspool.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, pointer, NativeMethods.NullHandleRef, SafeNativeMethods.DM_OUT_BUFFER); + if (returnCode < 0) + { + throw new Win32Exception(); + } + } + + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(pointer)!; + + if (_extrainfo != null) + { + // guard against buffer overrun attacks (since design allows client to set a new printer name without updating the devmode) + // by checking for a large enough buffer size before copying the extrainfo buffer + if (_extrabytes <= mode.dmDriverExtra) + { + IntPtr pointeroffset = (IntPtr)((byte*)pointer + mode.dmSize); + Marshal.Copy(_extrainfo, 0, pointeroffset, _extrabytes); + } + } + if ((mode.dmFields & SafeNativeMethods.DM_COPIES) == SafeNativeMethods.DM_COPIES) + { + if (_copies != -1) + mode.dmCopies = _copies; + } + + if ((mode.dmFields & SafeNativeMethods.DM_DUPLEX) == SafeNativeMethods.DM_DUPLEX) + { + if (unchecked((int)_duplex) != -1) + mode.dmDuplex = unchecked((short)_duplex); + } + + if ((mode.dmFields & SafeNativeMethods.DM_COLLATE) == SafeNativeMethods.DM_COLLATE) + { + if (_collate.IsNotDefault) + mode.dmCollate = (short)(((bool)_collate) ? SafeNativeMethods.DMCOLLATE_TRUE : SafeNativeMethods.DMCOLLATE_FALSE); + } + + Marshal.StructureToPtr(mode, pointer, false); + + int retCode = Interop.Winspool.DocumentProperties(NativeMethods.NullHandleRef, NativeMethods.NullHandleRef, printer, pointer, pointer, SafeNativeMethods.DM_IN_BUFFER | SafeNativeMethods.DM_OUT_BUFFER); + if (retCode < 0) + { + Interop.Kernel32.GlobalFree(handle); + Interop.Kernel32.GlobalUnlock(handle); + return IntPtr.Zero; + } + + + Interop.Kernel32.GlobalUnlock(handle); + return handle; + } + + /// + /// Creates a handle to a DEVMODE structure which correspond to the printer and page settings. + /// When you are done with the handle, you must deallocate it yourself: + /// Interop.Kernel32.GlobalFree(handle); + /// Where "handle" is the return value from this method. + /// + public IntPtr GetHdevmode(PageSettings pageSettings) + { + IntPtr modeHandle = GetHdevmodeInternal(); + pageSettings.CopyToHdevmode(modeHandle); + + return modeHandle; + } + + /// + /// Creates a handle to a DEVNAMES structure which correspond to the printer settings. + /// When you are done with the handle, you must deallocate it yourself: + /// Interop.Kernel32.GlobalFree(handle); + /// Where "handle" is the return value from this method. + /// + public unsafe IntPtr GetHdevnames() + { + string printerName = PrinterName; // the PrinterName property is slow when using the default printer + string driver = DriverName; // make sure we are writing out exactly the same string as we got the length of + string outPort = OutputPort; + + // Create DEVNAMES structure + // +4 for null terminator + int namesCharacters = checked(4 + printerName.Length + driver.Length + outPort.Length); + + // 8 = size of fixed portion of DEVNAMES + short offset = (short)(8 / Marshal.SystemDefaultCharSize); // Offsets are in characters, not bytes + uint namesSize = (uint)checked(Marshal.SystemDefaultCharSize * (offset + namesCharacters)); // always >0 + IntPtr handle = Interop.Kernel32.GlobalAlloc(SafeNativeMethods.GMEM_MOVEABLE | SafeNativeMethods.GMEM_ZEROINIT, namesSize); + IntPtr namesPointer = Interop.Kernel32.GlobalLock(handle); + byte* pNamesPointer = (byte*)namesPointer; + + *(short*)(pNamesPointer) = offset; // wDriverOffset + offset += WriteOneDEVNAME(driver, namesPointer, offset); + *(short*)(pNamesPointer + 2) = offset; // wDeviceOffset + offset += WriteOneDEVNAME(printerName, namesPointer, offset); + *(short*)(pNamesPointer + 4) = offset; // wOutputOffset + offset += WriteOneDEVNAME(outPort, namesPointer, offset); + *(short*)(pNamesPointer + 6) = offset; // wDefault + + Interop.Kernel32.GlobalUnlock(handle); + return handle; + } + + // Handles creating then disposing a default DEVMODE + internal short GetModeField(ModeField field, short defaultValue) + { + return GetModeField(field, defaultValue, IntPtr.Zero); + } + + internal short GetModeField(ModeField field, short defaultValue, IntPtr modeHandle) + { + bool ownHandle = false; + short result; + try + { + if (modeHandle == IntPtr.Zero) + { + try + { + modeHandle = GetHdevmodeInternal(); + ownHandle = true; + } + catch (InvalidPrinterException) + { + return defaultValue; + } + } + + IntPtr modePointer = Interop.Kernel32.GlobalLock(new HandleRef(this, modeHandle)); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(modePointer)!; + switch (field) + { + case ModeField.Orientation: + result = mode.dmOrientation; + break; + case ModeField.PaperSize: + result = mode.dmPaperSize; + break; + case ModeField.PaperLength: + result = mode.dmPaperLength; + break; + case ModeField.PaperWidth: + result = mode.dmPaperWidth; + break; + case ModeField.Copies: + result = mode.dmCopies; + break; + case ModeField.DefaultSource: + result = mode.dmDefaultSource; + break; + case ModeField.PrintQuality: + result = mode.dmPrintQuality; + break; + case ModeField.Color: + result = mode.dmColor; + break; + case ModeField.Duplex: + result = mode.dmDuplex; + break; + case ModeField.YResolution: + result = mode.dmYResolution; + break; + case ModeField.TTOption: + result = mode.dmTTOption; + break; + case ModeField.Collate: + result = mode.dmCollate; + break; + default: + Debug.Fail("Invalid field in GetModeField"); + result = defaultValue; + break; + } + Interop.Kernel32.GlobalUnlock(new HandleRef(this, modeHandle)); + } + finally + { + if (ownHandle) + { + Interop.Kernel32.GlobalFree(new HandleRef(this, modeHandle)); + } + } + return result; + } + + internal unsafe PaperSize[] Get_PaperSizes() + { + string printerName = PrinterName; // this is quite expensive if PrinterName is left default + + int count = FastDeviceCapabilities(SafeNativeMethods.DC_PAPERNAMES, IntPtr.Zero, -1, printerName); + if (count == -1) + return Array.Empty(); + int stringSize = Marshal.SystemDefaultCharSize * 64; + IntPtr namesBuffer = Marshal.AllocCoTaskMem(checked(stringSize * count)); + FastDeviceCapabilities(SafeNativeMethods.DC_PAPERNAMES, namesBuffer, -1, printerName); + + Debug.Assert(FastDeviceCapabilities(SafeNativeMethods.DC_PAPERS, IntPtr.Zero, -1, printerName) == count, + "Not the same number of paper kinds as paper names?"); + IntPtr kindsBuffer = Marshal.AllocCoTaskMem(2 * count); + FastDeviceCapabilities(SafeNativeMethods.DC_PAPERS, kindsBuffer, -1, printerName); + + Debug.Assert(FastDeviceCapabilities(SafeNativeMethods.DC_PAPERSIZE, IntPtr.Zero, -1, printerName) == count, + "Not the same number of paper kinds as paper names?"); + IntPtr dimensionsBuffer = Marshal.AllocCoTaskMem(8 * count); + FastDeviceCapabilities(SafeNativeMethods.DC_PAPERSIZE, dimensionsBuffer, -1, printerName); + + PaperSize[] result = new PaperSize[count]; + byte* pNamesBuffer = (byte*)namesBuffer; + short* pKindsBuffer = (short*)kindsBuffer; + int* pDimensionsBuffer = (int*)dimensionsBuffer; + for (int i = 0; i < count; i++) + { + string name = Marshal.PtrToStringAuto((nint)(pNamesBuffer + stringSize * (nint)i), 64)!; + int index = name.IndexOf('\0'); + if (index > -1) + { + name = name.Substring(0, index); + } + short kind = pKindsBuffer[i]; + int width = pDimensionsBuffer[i * 2]; + int height = pDimensionsBuffer[i * 2 + 1]; + result[i] = new PaperSize((PaperKind)kind, name, + PrinterUnitConvert.Convert(width, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display), + PrinterUnitConvert.Convert(height, PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display)); + } + + Marshal.FreeCoTaskMem(namesBuffer); + Marshal.FreeCoTaskMem(kindsBuffer); + Marshal.FreeCoTaskMem(dimensionsBuffer); + return result; + } + + internal unsafe PaperSource[] Get_PaperSources() + { + string printerName = PrinterName; // this is quite expensive if PrinterName is left default + + int count = FastDeviceCapabilities(SafeNativeMethods.DC_BINNAMES, IntPtr.Zero, -1, printerName); + if (count == -1) + return Array.Empty(); + + // Contrary to documentation, DeviceCapabilities returns char[count, 24], + // not char[count][24] + int stringSize = Marshal.SystemDefaultCharSize * 24; + IntPtr namesBuffer = Marshal.AllocCoTaskMem(checked(stringSize * count)); + FastDeviceCapabilities(SafeNativeMethods.DC_BINNAMES, namesBuffer, -1, printerName); + + Debug.Assert(FastDeviceCapabilities(SafeNativeMethods.DC_BINS, IntPtr.Zero, -1, printerName) == count, + "Not the same number of bin kinds as bin names?"); + IntPtr kindsBuffer = Marshal.AllocCoTaskMem(2 * count); + FastDeviceCapabilities(SafeNativeMethods.DC_BINS, kindsBuffer, -1, printerName); + + byte* pNamesBuffer = (byte*)namesBuffer; + short* pKindsBuffer = (short*)kindsBuffer; + PaperSource[] result = new PaperSource[count]; + for (int i = 0; i < count; i++) + { + string name = Marshal.PtrToStringAuto((nint)(pNamesBuffer + stringSize * (nint)i), 24)!; + int index = name.IndexOf('\0'); + if (index > -1) + { + name = name.Substring(0, index); + } + + short kind = pKindsBuffer[i]; + result[i] = new PaperSource((PaperSourceKind)kind, name); + } + + Marshal.FreeCoTaskMem(namesBuffer); + Marshal.FreeCoTaskMem(kindsBuffer); + return result; + } + + internal unsafe PrinterResolution[] Get_PrinterResolutions() + { + string printerName = PrinterName; // this is quite expensive if PrinterName is left default + PrinterResolution[] result; + + int count = FastDeviceCapabilities(SafeNativeMethods.DC_ENUMRESOLUTIONS, IntPtr.Zero, -1, printerName); + if (count == -1) + { + //Just return the standard values if custom resolutions are absent .... + result = new PrinterResolution[4]; + result[0] = new PrinterResolution(PrinterResolutionKind.High, -4, -1); + result[1] = new PrinterResolution(PrinterResolutionKind.Medium, -3, -1); + result[2] = new PrinterResolution(PrinterResolutionKind.Low, -2, -1); + result[3] = new PrinterResolution(PrinterResolutionKind.Draft, -1, -1); + + return result; + } + + result = new PrinterResolution[count + 4]; + result[0] = new PrinterResolution(PrinterResolutionKind.High, -4, -1); + result[1] = new PrinterResolution(PrinterResolutionKind.Medium, -3, -1); + result[2] = new PrinterResolution(PrinterResolutionKind.Low, -2, -1); + result[3] = new PrinterResolution(PrinterResolutionKind.Draft, -1, -1); + + IntPtr buffer = Marshal.AllocCoTaskMem(checked(8 * count)); + FastDeviceCapabilities(SafeNativeMethods.DC_ENUMRESOLUTIONS, buffer, -1, printerName); + + byte* pBuffer = (byte*)buffer; + for (int i = 0; i < count; i++) + { + int x = *(int*)(pBuffer + i * 8); + int y = *(int*)(pBuffer + i * 8 + 4); + result[i + 4] = new PrinterResolution(PrinterResolutionKind.Custom, x, y); + } + + Marshal.FreeCoTaskMem(buffer); + return result; + } + + // names is pointer to DEVNAMES + private static unsafe string ReadOneDEVNAME(IntPtr pDevnames, int slot) + { + byte* bDevNames = (byte*)pDevnames; + int offset = Marshal.SystemDefaultCharSize * ((ushort*)bDevNames)[slot]; + string result = Marshal.PtrToStringAuto((nint)(bDevNames + offset))!; + return result; + } + + /// + /// Copies the relevant information out of the handle and into the PrinterSettings. + /// + public unsafe void SetHdevmode(IntPtr hdevmode) + { + if (hdevmode == IntPtr.Zero) + throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevmode)); + + IntPtr pointer = Interop.Kernel32.GlobalLock(hdevmode); + Interop.Gdi32.DEVMODE mode = Marshal.PtrToStructure(pointer)!; + + //Copy entire public devmode as a byte array... + _devmodebytes = mode.dmSize; + if (_devmodebytes > 0) + { + _cachedDevmode = new byte[_devmodebytes]; + Marshal.Copy(pointer, _cachedDevmode, 0, _devmodebytes); + } + + //Copy private devmode as a byte array.. + _extrabytes = mode.dmDriverExtra; + if (_extrabytes > 0) + { + _extrainfo = new byte[_extrabytes]; + Marshal.Copy((nint)((byte*)pointer + mode.dmSize), _extrainfo, 0, _extrabytes); + } + + if ((mode.dmFields & SafeNativeMethods.DM_COPIES) == SafeNativeMethods.DM_COPIES) + { + _copies = mode.dmCopies; + } + + if ((mode.dmFields & SafeNativeMethods.DM_DUPLEX) == SafeNativeMethods.DM_DUPLEX) + { + _duplex = (Duplex)mode.dmDuplex; + } + + if ((mode.dmFields & SafeNativeMethods.DM_COLLATE) == SafeNativeMethods.DM_COLLATE) + { + _collate = (mode.dmCollate == SafeNativeMethods.DMCOLLATE_TRUE); + } + + Interop.Kernel32.GlobalUnlock(hdevmode); + } + + /// + /// Copies the relevant information out of the handle and into the PrinterSettings. + /// + public void SetHdevnames(IntPtr hdevnames) + { + if (hdevnames == IntPtr.Zero) + { + throw new ArgumentException(SR.Format(SR.InvalidPrinterHandle, hdevnames)); + } + + IntPtr namesPointer = Interop.Kernel32.GlobalLock(hdevnames); + + _driverName = ReadOneDEVNAME(namesPointer, 0); + _printerName = ReadOneDEVNAME(namesPointer, 1); + _outputPort = ReadOneDEVNAME(namesPointer, 2); + + PrintDialogDisplayed = true; + + Interop.Kernel32.GlobalUnlock(hdevnames); + } + + /// + /// Provides some interesting information about the PrinterSettings in String form. + /// + public override string ToString() + { + string printerName = PrinterName; + return "[PrinterSettings " + + printerName + + " Copies=" + Copies.ToString(CultureInfo.InvariantCulture) + + " Collate=" + Collate.ToString(CultureInfo.InvariantCulture) + + " Duplex=" + Duplex.ToString() + + " FromPage=" + FromPage.ToString(CultureInfo.InvariantCulture) + + " LandscapeAngle=" + LandscapeAngle.ToString(CultureInfo.InvariantCulture) + + " MaximumCopies=" + MaximumCopies.ToString(CultureInfo.InvariantCulture) + + " OutputPort=" + OutputPort.ToString(CultureInfo.InvariantCulture) + + " ToPage=" + ToPage.ToString(CultureInfo.InvariantCulture) + + "]"; + } + + // Write null terminated string, return length of string in characters (including null) + private static unsafe short WriteOneDEVNAME(string str, IntPtr bufferStart, int index) + { + str ??= ""; + byte* address = (byte*)bufferStart + (nint)index * Marshal.SystemDefaultCharSize; + + char[] data = str.ToCharArray(); + Marshal.Copy(data, 0, (nint)address, data.Length); + *(short*)(address + data.Length * 2) = 0; + + return checked((short)(str.Length + 1)); + } + + /// + /// Collection of PaperSize's... + /// + public class PaperSizeCollection : ICollection + { + private PaperSize[] _array; + + /// + /// Initializes a new instance of the class. + /// + public PaperSizeCollection(PaperSize[] array) + { + _array = array; + } + + /// + /// Gets a value indicating the number of paper sizes. + /// + public int Count + { + get + { + return _array.Length; + } + } + + /// + /// Retrieves the PaperSize with the specified index. + /// + public virtual PaperSize this[int index] + { + get + { + return _array[index]; + } + } + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_array, Count); + } + + int ICollection.Count + { + get + { + return Count; + } + } + + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + return this; + } + } + + void ICollection.CopyTo(Array array, int index) + { + Array.Copy(_array, index, array, 0, _array.Length); + } + + public void CopyTo(PaperSize[] paperSizes, int index) + { + Array.Copy(_array, index, paperSizes, 0, _array.Length); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public int Add(PaperSize paperSize) + { + PaperSize[] newArray = new PaperSize[Count + 1]; + ((ICollection)this).CopyTo(newArray, 0); + newArray[Count] = paperSize; + _array = newArray; + return Count; + } + } + + public class PaperSourceCollection : ICollection + { + private PaperSource[] _array; + + /// + /// Initializes a new instance of the class. + /// + public PaperSourceCollection(PaperSource[] array) + { + _array = array; + } + + /// + /// Gets a value indicating the number of paper sources. + /// + public int Count + { + get + { + return _array.Length; + } + } + + /// + /// Gets the PaperSource with the specified index. + /// + public virtual PaperSource this[int index] + { + get + { + return _array[index]; + } + } + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_array, Count); + } + + int ICollection.Count + { + get + { + return Count; + } + } + + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + return this; + } + } + + void ICollection.CopyTo(Array array, int index) + { + Array.Copy(_array, index, array, 0, _array.Length); + } + + public void CopyTo(PaperSource[] paperSources, int index) + { + Array.Copy(_array, index, paperSources, 0, _array.Length); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public int Add(PaperSource paperSource) + { + PaperSource[] newArray = new PaperSource[Count + 1]; + ((ICollection)this).CopyTo(newArray, 0); + newArray[Count] = paperSource; + _array = newArray; + return Count; + } + } + + public class PrinterResolutionCollection : ICollection + { + private PrinterResolution[] _array; + + /// + /// Initializes a new instance of the class. + /// + public PrinterResolutionCollection(PrinterResolution[] array) + { + _array = array; + } + + /// + /// Gets a value indicating the number of available printer resolutions. + /// + public int Count + { + get + { + return _array.Length; + } + } + + /// + /// Retrieves the PrinterResolution with the specified index. + /// + public virtual PrinterResolution this[int index] + { + get + { + return _array[index]; + } + } + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_array, Count); + } + + int ICollection.Count + { + get + { + return Count; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + return this; + } + } + + void ICollection.CopyTo(Array array, int index) + { + Array.Copy(_array, index, array, 0, _array.Length); + } + + public void CopyTo(PrinterResolution[] printerResolutions, int index) + { + Array.Copy(_array, index, printerResolutions, 0, _array.Length); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public int Add(PrinterResolution printerResolution) + { + PrinterResolution[] newArray = new PrinterResolution[Count + 1]; + ((ICollection)this).CopyTo(newArray, 0); + newArray[Count] = printerResolution; + _array = newArray; + return Count; + } + } + + public class StringCollection : ICollection + { + private string[] _array; + + /// + /// Initializes a new instance of the class. + /// + public StringCollection(string[] array) + { + _array = array; + } + + /// + /// Gets a value indicating the number of strings. + /// + public int Count + { + get + { + return _array.Length; + } + } + + /// + /// Gets the string with the specified index. + /// + public virtual string this[int index] + { + get + { + return _array[index]; + } + } + + public IEnumerator GetEnumerator() + { + return new ArrayEnumerator(_array, Count); + } + + int ICollection.Count + { + get + { + return Count; + } + } + + bool ICollection.IsSynchronized + { + get + { + return false; + } + } + + object ICollection.SyncRoot + { + get + { + return this; + } + } + + void ICollection.CopyTo(Array array, int index) + { + Array.Copy(_array, index, array, 0, _array.Length); + } + + + public void CopyTo(string[] strings, int index) + { + Array.Copy(_array, index, strings, 0, _array.Length); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + [ + EditorBrowsable(EditorBrowsableState.Never) + ] + public int Add(string value) + { + string[] newArray = new string[Count + 1]; + ((ICollection)this).CopyTo(newArray, 0); + newArray[Count] = value; + _array = newArray; + return Count; + } + } + + private sealed class ArrayEnumerator : IEnumerator + { + private readonly object[] _array; + private readonly int _endIndex; + private int _index; + private object? _item; + + public ArrayEnumerator(object[] array, int count) + { + _array = array; + _endIndex = count; + } + + public object? Current => _item; + + public bool MoveNext() + { + if (_index >= _endIndex) + return false; + _item = _array[_index++]; + return true; + } + + public void Reset() + { + // Position enumerator before first item + _index = 0; + _item = null; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnit.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnit.cs new file mode 100644 index 00000000000..e0e80c46cfc --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnit.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Specifies several of the units of measure Microsoft Win32 uses for printing. + /// + public enum PrinterUnit + { + /// + /// The default unit (0.01 in.). + /// + Display = 0, + + /// + /// One thousandth of an inch (0.001 in.). + /// + // Used by PAGESETUPDLG.rtMargin and rtMinMargin + ThousandthsOfAnInch = 1, + + /// + /// One hundredth of a millimeter (0.01 mm). + /// + // Used by PAGESETUPDLG.rtMargin and rtMinMargin + HundredthsOfAMillimeter = 2, + + /// + /// One tenth of a millimeter (0.1 mm). + /// + // DeviceCapabilities(DC_PAPERSIZE) + TenthsOfAMillimeter = 3, + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnitConvert.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnitConvert.cs new file mode 100644 index 00000000000..c943ec3c969 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/PrinterUnitConvert.cs @@ -0,0 +1,112 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Drawing.Printing +{ + /// + /// Specifies a series of conversion methods that are useful when interoperating with the raw Win32 printing API. + /// This class cannot be inherited. + /// + public sealed class PrinterUnitConvert + { + private PrinterUnitConvert() + { + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static double Convert(double value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + double fromUnitsPerDisplay = UnitsPerDisplay(fromUnit); + double toUnitsPerDisplay = UnitsPerDisplay(toUnit); + return value * toUnitsPerDisplay / fromUnitsPerDisplay; + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static int Convert(int value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + return (int)Math.Round(Convert((double)value, fromUnit, toUnit)); + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static Point Convert(Point value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + return new Point( + Convert(value.X, fromUnit, toUnit), + Convert(value.Y, fromUnit, toUnit) + ); + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static Size Convert(Size value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + return new Size( + Convert(value.Width, fromUnit, toUnit), + Convert(value.Height, fromUnit, toUnit) + ); + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static Rectangle Convert(Rectangle value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + return new Rectangle( + Convert(value.X, fromUnit, toUnit), + Convert(value.Y, fromUnit, toUnit), + Convert(value.Width, fromUnit, toUnit), + Convert(value.Height, fromUnit, toUnit) + ); + } + + /// + /// Converts the value, in fromUnit units, to toUnit units. + /// + public static Margins Convert(Margins value, PrinterUnit fromUnit, PrinterUnit toUnit) + { + Margins result = new Margins(); + + result.DoubleLeft = Convert(value.DoubleLeft, fromUnit, toUnit); + result.DoubleRight = Convert(value.DoubleRight, fromUnit, toUnit); + result.DoubleTop = Convert(value.DoubleTop, fromUnit, toUnit); + result.DoubleBottom = Convert(value.DoubleBottom, fromUnit, toUnit); + + return result; + } + + private static double UnitsPerDisplay(PrinterUnit unit) + { + double result; + switch (unit) + { + case PrinterUnit.Display: + result = 1.0; + break; + case PrinterUnit.ThousandthsOfAnInch: + result = 10.0; + break; + case PrinterUnit.HundredthsOfAMillimeter: + result = 25.4; + break; + case PrinterUnit.TenthsOfAMillimeter: + result = 2.54; + break; + default: + Debug.Fail("Unknown PrinterUnit " + unit); + result = 1.0; + break; + } + + return result; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventArgs.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventArgs.cs new file mode 100644 index 00000000000..ea8f3e5dfc2 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventArgs.cs @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Provides data for the event. + /// + public class QueryPageSettingsEventArgs : PrintEventArgs + { + private PageSettings _pageSettings; + + /// + /// It's too expensive to compare 2 instances of PageSettings class, as the getters + /// are accessing the printer spooler, thus we track any explicit invocations of the setters or getters on this class, + /// and this field tracks if PageSettings property was accessed. It will return a false + /// positive when the user is reading property values, but we'll take a perf hit in this case assuming this event is not + /// used often. + /// + internal bool PageSettingsChanged; + + /// + /// Initializes a new instance of the class. + /// + public QueryPageSettingsEventArgs(PageSettings pageSettings) : base() + { + _pageSettings = pageSettings; + } + + /// + /// Gets or sets the page settings for the page to be printed. + /// + public PageSettings PageSettings + { + get + { + PageSettingsChanged = true; + return _pageSettings; + } + set + { + value ??= new PageSettings(); + _pageSettings = value; + PageSettingsChanged = true; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventHandler.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventHandler.cs new file mode 100644 index 00000000000..cb9d6f06226 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/QueryPageSettingsEventHandler.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Printing +{ + /// + /// Represents the method that will handle the event of a + /// . + /// + public delegate void QueryPageSettingsEventHandler(object sender, QueryPageSettingsEventArgs e); +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs b/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs new file mode 100644 index 00000000000..bb7b163d1ce --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Printing/TriState.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +namespace System.Drawing.Printing +{ + internal readonly partial struct TriState : IEquatable + { + private readonly byte _value; // 0 is "default", not false + + public static readonly TriState Default = new TriState(0); + public static readonly TriState False = new TriState(1); + public static readonly TriState True = new TriState(2); + + private TriState(byte value) => _value = value; + + public bool IsDefault => this == Default; + + public bool IsFalse => this == False; + + public bool IsNotDefault => this != Default; + + public bool IsTrue => this == True; + + public static bool operator ==(TriState left, TriState right) => left.Equals(right); + + public static bool operator !=(TriState left, TriState right) => !left.Equals(right); + + public override bool Equals([NotNullWhen(true)] object? o) + { + Debug.Assert(o is TriState); + return Equals((TriState)o); + } + + public bool Equals(TriState other) => _value == other._value; + + public override int GetHashCode() => _value; + + public static implicit operator TriState(bool value) => value ? True : False; + + public static explicit operator bool(TriState value) + { + if (value.IsDefault) + { + throw new InvalidCastException(SR.TriStateCompareError); + } + + return (value == TriState.True); + } + + /// Provides some interesting information about the TriState in String form. + public override string ToString() => + this == Default ? "Default" : + this == False ? "False" : + "True"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs b/src/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs new file mode 100644 index 00000000000..e64c042c6d9 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/PropertyItemInternal.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing.Imaging +{ + internal unsafe struct PropertyItemInternal + { + public int id; + public int len; + public short type; + public byte* value; + + public Span Value => new Span(value, len); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/RectangleConverter.cs b/src/System.Drawing.Common/src/System/Drawing/RectangleConverter.cs new file mode 100644 index 00000000000..b589d5fc15b --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/RectangleConverter.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Reflection; + + /// + /// + /// RectangleConverter is a class that can be used to convert + /// rectangles from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class RectangleConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, [NotNullWhen(true)] Type? destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + string strValue = value as string; + + if (strValue != null) { + + string text = strValue.Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 4 integer values. + // + culture ??= CultureInfo.CurrentCulture; + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(sep); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 4) { + return new Rectangle(values[0], values[1], values[2], values[3]); + } + else { + throw new ArgumentException(SR.Format(SR.TextParseFailedFormat, + text, + "x, y, width, height")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the destination type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException(nameof(destinationType)); + } + + if ( value is Rectangle ){ + if (destinationType == typeof(string)) { + Rectangle rect = (Rectangle)value; + + culture ??= CultureInfo.CurrentCulture; + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + string[] args = new string[4]; + int nArg = 0; + + // Note: ConvertToString will raise exception if value cannot be converted. + args[nArg++] = intConverter.ConvertToString(context, culture, rect.X); + args[nArg++] = intConverter.ConvertToString(context, culture, rect.Y); + args[nArg++] = intConverter.ConvertToString(context, culture, rect.Width); + args[nArg++] = intConverter.ConvertToString(context, culture, rect.Height); + + return string.Join(sep, args); + } + if (destinationType == typeof(InstanceDescriptor)) { + Rectangle rect = (Rectangle)value; + ConstructorInfo ctor = typeof(Rectangle).GetConstructor(new Type[] { + typeof(int), typeof(int), typeof(int), typeof(int)}); + + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] { + rect.X, rect.Y, rect.Width, rect.Height}); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + if ( propertyValues == null ){ + throw new ArgumentNullException( nameof(propertyValues) ); + } + + object x = propertyValues["X"]; + object y = propertyValues["Y"]; + object width = propertyValues["Width"]; + object height = propertyValues["Height"]; + + if (x == null || y == null || width == null || height == null || + !(x is int) || !(y is int) || !(width is int) || !(height is int) ) { + throw new ArgumentException(SR.PropertyValueInvalidEntry); + } + return new Rectangle((int)x, + (int)y, + (int)width, + (int)height); + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Rectangle), attributes); + return props.Sort(new string[] {"X", "Y", "Width", "Height"}); + } + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Region.cs b/src/System.Drawing.Common/src/System/Drawing/Region.cs new file mode 100644 index 00000000000..f7ff48efb3d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Region.cs @@ -0,0 +1,428 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Drawing2D; +using System.Drawing.Internal; +using System.Globalization; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public sealed class Region : MarshalByRefObject, IDisposable + { +#if FINALIZATION_WATCH + private string allocationSite = Graphics.GetAllocationStack(); +#endif + + internal IntPtr NativeRegion { get; private set; } + + public Region() + { + Gdip.CheckStatus(Gdip.GdipCreateRegion(out IntPtr region)); + SetNativeRegion(region); + } + + public Region(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCreateRegionRect(ref rect, out IntPtr region)); + SetNativeRegion(region); + } + + public Region(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCreateRegionRectI(ref rect, out IntPtr region)); + SetNativeRegion(region); + } + + public Region(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCreateRegionPath(new HandleRef(path, path._nativePath), out IntPtr region)); + SetNativeRegion(region); + } + + public Region(RegionData rgnData) + { + ArgumentNullException.ThrowIfNull(rgnData); + + Gdip.CheckStatus(Gdip.GdipCreateRegionRgnData( + rgnData.Data, + rgnData.Data.Length, + out IntPtr region)); + + SetNativeRegion(region); + } + + internal Region(IntPtr nativeRegion) => SetNativeRegion(nativeRegion); + + public static Region FromHrgn(IntPtr hrgn) + { + Gdip.CheckStatus(Gdip.GdipCreateRegionHrgn(hrgn, out IntPtr region)); + return new Region(region); + } + + private void SetNativeRegion(IntPtr nativeRegion) + { + if (nativeRegion == IntPtr.Zero) + throw new ArgumentNullException(nameof(nativeRegion)); + + NativeRegion = nativeRegion; + } + + public Region Clone() + { + Gdip.CheckStatus(Gdip.GdipCloneRegion(new HandleRef(this, NativeRegion), out IntPtr region)); + return new Region(region); + } + public void ReleaseHrgn(IntPtr regionHandle) + { + if (regionHandle == IntPtr.Zero) + { + throw new ArgumentNullException(nameof(regionHandle)); + } + + Interop.Gdi32.DeleteObject(new HandleRef(this, regionHandle)); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { +#if FINALIZATION_WATCH + if (!disposing && nativeRegion != IntPtr.Zero) + Debug.WriteLine("**********************\nDisposed through finalization:\n" + allocationSite); +#endif + if (NativeRegion != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteRegion(new HandleRef(this, NativeRegion)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + } + finally + { + NativeRegion = IntPtr.Zero; + } + } + } + + ~Region() => Dispose(false); + + public void MakeInfinite() + { + Gdip.CheckStatus(Gdip.GdipSetInfinite(new HandleRef(this, NativeRegion))); + } + + public void MakeEmpty() + { + Gdip.CheckStatus(Gdip.GdipSetEmpty(new HandleRef(this, NativeRegion))); + } + + public void Intersect(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRect(new HandleRef(this, NativeRegion), ref rect, CombineMode.Intersect)); + } + + public void Intersect(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRectI(new HandleRef(this, NativeRegion), ref rect, CombineMode.Intersect)); + } + + public void Intersect(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCombineRegionPath(new HandleRef(this, NativeRegion), new HandleRef(path, path._nativePath), CombineMode.Intersect)); + } + + public void Intersect(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipCombineRegionRegion(new HandleRef(this, NativeRegion), new HandleRef(region, region.NativeRegion), CombineMode.Intersect)); + } + + public void Union(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRect(new HandleRef(this, NativeRegion), ref rect, CombineMode.Union)); + } + + public void Union(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRectI(new HandleRef(this, NativeRegion), ref rect, CombineMode.Union)); + } + + public void Union(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCombineRegionPath(new HandleRef(this, NativeRegion), new HandleRef(path, path._nativePath), CombineMode.Union)); + } + + public void Union(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipCombineRegionRegion(new HandleRef(this, NativeRegion), new HandleRef(region, region.NativeRegion), CombineMode.Union)); + } + + public void Xor(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRect(new HandleRef(this, NativeRegion), ref rect, CombineMode.Xor)); + } + + public void Xor(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRectI(new HandleRef(this, NativeRegion), ref rect, CombineMode.Xor)); + } + + public void Xor(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCombineRegionPath(new HandleRef(this, NativeRegion), new HandleRef(path, path._nativePath), CombineMode.Xor)); + } + + public void Xor(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipCombineRegionRegion(new HandleRef(this, NativeRegion), new HandleRef(region, region.NativeRegion), CombineMode.Xor)); + } + + public void Exclude(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRect(new HandleRef(this, NativeRegion), ref rect, CombineMode.Exclude)); + } + + public void Exclude(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRectI(new HandleRef(this, NativeRegion), ref rect, CombineMode.Exclude)); + } + + public void Exclude(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCombineRegionPath( + new HandleRef(this, NativeRegion), + new HandleRef(path, path._nativePath), + CombineMode.Exclude)); + } + + public void Exclude(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipCombineRegionRegion( + new HandleRef(this, NativeRegion), + new HandleRef(region, region.NativeRegion), + CombineMode.Exclude)); + } + + public void Complement(RectangleF rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRect(new HandleRef(this, NativeRegion), ref rect, CombineMode.Complement)); + } + + public void Complement(Rectangle rect) + { + Gdip.CheckStatus(Gdip.GdipCombineRegionRectI(new HandleRef(this, NativeRegion), ref rect, CombineMode.Complement)); + } + + public void Complement(GraphicsPath path) + { + ArgumentNullException.ThrowIfNull(path); + + Gdip.CheckStatus(Gdip.GdipCombineRegionPath(new HandleRef(this, NativeRegion), new HandleRef(path, path._nativePath), CombineMode.Complement)); + } + + public void Complement(Region region) + { + ArgumentNullException.ThrowIfNull(region); + + Gdip.CheckStatus(Gdip.GdipCombineRegionRegion(new HandleRef(this, NativeRegion), new HandleRef(region, region.NativeRegion), CombineMode.Complement)); + } + + public void Translate(float dx, float dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateRegion(new HandleRef(this, NativeRegion), dx, dy)); + } + + public void Translate(int dx, int dy) + { + Gdip.CheckStatus(Gdip.GdipTranslateRegionI(new HandleRef(this, NativeRegion), dx, dy)); + } + + public void Transform(Matrix matrix) + { + ArgumentNullException.ThrowIfNull(matrix); + + Gdip.CheckStatus(Gdip.GdipTransformRegion( + new HandleRef(this, NativeRegion), + new HandleRef(matrix, matrix.NativeMatrix))); + } + + public RectangleF GetBounds(Graphics g) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipGetRegionBounds(new HandleRef(this, NativeRegion), new HandleRef(g, g.NativeGraphics), out RectangleF bounds)); + return bounds; + } + + public IntPtr GetHrgn(Graphics g) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipGetRegionHRgn(new HandleRef(this, NativeRegion), new HandleRef(g, g.NativeGraphics), out IntPtr hrgn)); + return hrgn; + } + + public bool IsEmpty(Graphics g) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipIsEmptyRegion(new HandleRef(this, NativeRegion), new HandleRef(g, g.NativeGraphics), out int isEmpty)); + return isEmpty != 0; + } + + public bool IsInfinite(Graphics g) + { + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipIsInfiniteRegion(new HandleRef(this, NativeRegion), new HandleRef(g, g.NativeGraphics), out int isInfinite)); + return isInfinite != 0; + } + + public bool Equals(Region region, Graphics g) + { + ArgumentNullException.ThrowIfNull(region); + ArgumentNullException.ThrowIfNull(g); + + Gdip.CheckStatus(Gdip.GdipIsEqualRegion(new HandleRef(this, NativeRegion), new HandleRef(region, region.NativeRegion), new HandleRef(g, g.NativeGraphics), out int isEqual)); + return isEqual != 0; + } + + public RegionData? GetRegionData() + { + Gdip.CheckStatus(Gdip.GdipGetRegionDataSize(new HandleRef(this, NativeRegion), out int regionSize)); + + if (regionSize == 0) + return null; + + byte[] regionData = new byte[regionSize]; + Gdip.CheckStatus(Gdip.GdipGetRegionData(new HandleRef(this, NativeRegion), regionData, regionSize, out _)); + return new RegionData(regionData); + } + + public bool IsVisible(float x, float y) => IsVisible(new PointF(x, y), null); + + public bool IsVisible(PointF point) => IsVisible(point, null); + + public bool IsVisible(float x, float y, Graphics? g) => IsVisible(new PointF(x, y), g); + + public bool IsVisible(PointF point, Graphics? g) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRegionPoint( + new HandleRef(this, NativeRegion), + point.X, point.Y, + new HandleRef(g, g?.NativeGraphics ?? IntPtr.Zero), + out int isVisible)); + + return isVisible != 0; + } + + public bool IsVisible(float x, float y, float width, float height) => IsVisible(new RectangleF(x, y, width, height), null); + + public bool IsVisible(RectangleF rect) => IsVisible(rect, null); + + public bool IsVisible(float x, float y, float width, float height, Graphics? g) => IsVisible(new RectangleF(x, y, width, height), g); + + public bool IsVisible(RectangleF rect, Graphics? g) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRegionRect( + new HandleRef(this, NativeRegion), + rect.X, rect.Y, rect.Width, rect.Height, + new HandleRef(g, g?.NativeGraphics ?? IntPtr.Zero), + out int isVisible)); + + return isVisible != 0; + } + + public bool IsVisible(int x, int y, Graphics? g) => IsVisible(new Point(x, y), g); + + public bool IsVisible(Point point) => IsVisible(point, null); + + public bool IsVisible(Point point, Graphics? g) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRegionPointI( + new HandleRef(this, NativeRegion), + point.X, point.Y, + new HandleRef(g, g?.NativeGraphics ?? IntPtr.Zero), + out int isVisible)); + + return isVisible != 0; + } + + public bool IsVisible(int x, int y, int width, int height) => IsVisible(new Rectangle(x, y, width, height), null); + + public bool IsVisible(Rectangle rect) => IsVisible(rect, null); + + public bool IsVisible(int x, int y, int width, int height, Graphics? g) => IsVisible(new Rectangle(x, y, width, height), g); + + public bool IsVisible(Rectangle rect, Graphics? g) + { + Gdip.CheckStatus(Gdip.GdipIsVisibleRegionRectI( + new HandleRef(this, NativeRegion), + rect.X, rect.Y, rect.Width, rect.Height, + new HandleRef(g, g?.NativeGraphics ?? IntPtr.Zero), + out int isVisible)); + + return isVisible != 0; + } + + public unsafe RectangleF[] GetRegionScans(Matrix matrix) + { + ArgumentNullException.ThrowIfNull(matrix); + + Gdip.CheckStatus(Gdip.GdipGetRegionScansCount( + new HandleRef(this, NativeRegion), + out int count, + new HandleRef(matrix, matrix.NativeMatrix))); + + RectangleF[] rectangles = new RectangleF[count]; + + // Pinning an empty array gives null, libgdiplus doesn't like this. + // As invoking isn't necessary, just return the empty array. + if (count == 0) + return rectangles; + + fixed (RectangleF* r = rectangles) + { + Gdip.CheckStatus(Gdip.GdipGetRegionScans + (new HandleRef(this, NativeRegion), + r, + out count, + new HandleRef(matrix, matrix.NativeMatrix))); + } + + return rectangles; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/RotateFlipType.cs b/src/System.Drawing.Common/src/System/Drawing/RotateFlipType.cs new file mode 100644 index 00000000000..11bffa2b66a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/RotateFlipType.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the different patterns available 'RotateFlipType' objects. + /// + public enum RotateFlipType + { + RotateNoneFlipNone = 0, + Rotate90FlipNone = 1, + Rotate180FlipNone = 2, + Rotate270FlipNone = 3, + RotateNoneFlipX = 4, + Rotate90FlipX = 5, + Rotate180FlipX = 6, + Rotate270FlipX = 7, + RotateNoneFlipY = Rotate180FlipX, + Rotate90FlipY = Rotate270FlipX, + Rotate180FlipY = RotateNoneFlipX, + Rotate270FlipY = Rotate90FlipX, + RotateNoneFlipXY = Rotate180FlipNone, + Rotate90FlipXY = Rotate270FlipNone, + Rotate180FlipXY = RotateNoneFlipNone, + Rotate270FlipXY = Rotate90FlipNone + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SRDescriptionAttribute.cs b/src/System.Drawing.Common/src/System/Drawing/SRDescriptionAttribute.cs new file mode 100644 index 00000000000..00d110b34f3 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SRDescriptionAttribute.cs @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// +// System.Drawing.SRDescriptionAttribute.cs +// +// Authors: +// Andreas Nahr (ClassDevelopment@A-SoftTech.com) +// +// (C) 2004 Andreas Nahr +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.ComponentModel; + +namespace System.Drawing +{ + [AttributeUsage(AttributeTargets.All)] + internal sealed class SRDescriptionAttribute : DescriptionAttribute + { + public SRDescriptionAttribute(string description) + : base(description) + { + } + + public override string Description + { + get + { + return DescriptionValue; + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ScreenDC.cs b/src/System.Drawing.Common/src/System/Drawing/ScreenDC.cs new file mode 100644 index 00000000000..d19d9c1e796 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ScreenDC.cs @@ -0,0 +1,24 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + /// + /// Simple wrapper to create a screen HDC within a using statement. + /// + internal struct ScreenDC : IDisposable + { + private IntPtr _handle; + + public static ScreenDC Create() => new ScreenDC + { + _handle = Interop.User32.GetDC(IntPtr.Zero) + }; + + public static implicit operator IntPtr(ScreenDC screenDC) => screenDC._handle; + + public void Dispose() => Interop.User32.ReleaseDC(IntPtr.Zero, _handle); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SizeConverter.cs b/src/System.Drawing.Common/src/System/Drawing/SizeConverter.cs new file mode 100644 index 00000000000..caa46f7d032 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SizeConverter.cs @@ -0,0 +1,188 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing { + using System.Runtime.Serialization.Formatters; + using System.Runtime.InteropServices; + + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + + using Microsoft.Win32; + using System.Collections; + using System.ComponentModel; + using System.ComponentModel.Design.Serialization; + using System.Globalization; + using System.Reflection; + + /// + /// + /// SizeConverter is a class that can be used to convert + /// Size from one data type to another. Access this + /// class through the TypeDescriptor. + /// + public class SizeConverter : TypeConverter { + + /// + /// + /// Determines if this converter can convert an object in the given source + /// type to the native type of the converter. + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return base.CanConvertFrom(context, sourceType); + } + + /// + /// + /// Gets a value indicating whether this converter can + /// convert an object to the given destination type using the context. + /// + public override bool CanConvertTo(ITypeDescriptorContext context, [NotNullWhen(true)] Type? destinationType) { + if (destinationType == typeof(InstanceDescriptor)) { + return true; + } + return base.CanConvertTo(context, destinationType); + } + + /// + /// + /// Converts the given object to the converter's native type. + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) { + + string strValue = value as string; + + if (strValue != null) { + + string text = strValue.Trim(); + + if (text.Length == 0) { + return null; + } + else { + + // Parse 2 integer values. + // + culture ??= CultureInfo.CurrentCulture; + char sep = culture.TextInfo.ListSeparator[0]; + string[] tokens = text.Split(sep); + int[] values = new int[tokens.Length]; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + for (int i = 0; i < values.Length; i++) { + // Note: ConvertFromString will raise exception if value cannot be converted. + values[i] = (int)intConverter.ConvertFromString(context, culture, tokens[i]); + } + + if (values.Length == 2) { + return new Size(values[0], values[1]); + } + else { + throw new ArgumentException(SR.Format(SR.TextParseFailedFormat, + text, + "Width,Height")); + } + } + } + + return base.ConvertFrom(context, culture, value); + } + + /// + /// + /// Converts the given object to another type. The most common types to convert + /// are to and from a string object. The default implementation will make a call + /// to ToString on the object if the object is valid and if the destination + /// type is string. If this cannot convert to the destination type, this will + /// throw a NotSupportedException. + /// + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) { + if (destinationType == null) { + throw new ArgumentNullException(nameof(destinationType)); + } + + if (value is Size){ + if (destinationType == typeof(string)) { + Size size = (Size)value; + + culture ??= CultureInfo.CurrentCulture; + string sep = culture.TextInfo.ListSeparator + " "; + TypeConverter intConverter = TypeDescriptor.GetConverter(typeof(int)); + + // Note: ConvertToString will raise exception if value cannot be converted. + return + intConverter.ConvertToString(context, culture, size.Width) + + sep + + intConverter.ConvertToString(context, culture, size.Height); + args[nArg++] = intConverter.ConvertToString(context, culture, size.Width); + args[nArg++] = intConverter.ConvertToString(context, culture, size.Height); + } + if (destinationType == typeof(InstanceDescriptor)) { + Size size = (Size)value; + + ConstructorInfo ctor = typeof(Size).GetConstructor(new Type[] {typeof(int), typeof(int)}); + if (ctor != null) { + return new InstanceDescriptor(ctor, new object[] {size.Width, size.Height}); + } + } + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + /// + /// + /// Creates an instance of this type given a set of property values + /// for the object. This is useful for objects that are immutable, but still + /// want to provide changable properties. + /// + public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues) { + if ( propertyValues == null ){ + throw new ArgumentNullException( nameof(propertyValues) ); + } + + + object width = propertyValues["Width"]; + object height = propertyValues["Height"]; + + if (width == null || height == null || + !(width is int) || !(height is int)) { + throw new ArgumentException(SR.PropertyValueInvalidEntry); + } + return new Size((int)width, + (int)height); + } + + /// + /// + /// Determines if changing a value on this object should require a call to + /// CreateInstance to create a new value. + /// + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + /// + /// + /// Retrieves the set of properties for this type. By default, a type has + /// does not return any properties. An easy implementation of this method + /// can just call TypeDescriptor.GetProperties for the correct data type. + /// + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(Size), attributes); + return props.Sort(new string[] {"Width", "Height"}); + } + + + /// + /// + /// Determines if this object supports properties. By default, this + /// is false. + /// + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SolidBrush.cs b/src/System.Drawing.Common/src/System/Drawing/SolidBrush.cs new file mode 100644 index 00000000000..d19d983688d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SolidBrush.cs @@ -0,0 +1,124 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Drawing.Internal; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public sealed class SolidBrush : Brush, ISystemColorTracker + { + // GDI+ doesn't understand system colors, so we need to cache the value here. + private Color _color = Color.Empty; + private bool _immutable; + + public SolidBrush(Color color) + { + _color = color; + + IntPtr nativeBrush; + int status = Gdip.GdipCreateSolidFill(_color.ToArgb(), out nativeBrush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(nativeBrush); + + if (_color.IsSystemColor) + { + SystemColorTracker.Add(this); + } + } + + internal SolidBrush(Color color, bool immutable) : this(color) + { + _immutable = immutable; + } + + internal SolidBrush(IntPtr nativeBrush) + { + Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null."); + SetNativeBrushInternal(nativeBrush); + } + + public override object Clone() + { + IntPtr clonedBrush; + int status = Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out clonedBrush); + Gdip.CheckStatus(status); + + // Clones of immutable brushes are not immutable. + return new SolidBrush(clonedBrush); + } + + protected override void Dispose(bool disposing) + { + if (!disposing) + { + _immutable = false; + } + else if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, "Brush")); + } + + base.Dispose(disposing); + } + + public Color Color + { + get + { + if (_color == Color.Empty) + { + int colorARGB; + int status = Gdip.GdipGetSolidFillColor(new HandleRef(this, NativeBrush), out colorARGB); + Gdip.CheckStatus(status); + + _color = Color.FromArgb(colorARGB); + } + + // GDI+ doesn't understand system colors, so we can't use GdipGetSolidFillColor in the general case. + return _color; + } + + set + { + if (_immutable) + { + throw new ArgumentException(SR.Format(SR.CantChangeImmutableObjects, "Brush")); + } + + if (_color != value) + { + Color oldColor = _color; + InternalSetColor(value); + + // NOTE: We never remove brushes from the active list, so if someone is + // changing their brush colors a lot, this could be a problem. + if (value.IsSystemColor && !oldColor.IsSystemColor) + { + SystemColorTracker.Add(this); + } + } + } + } + + // Sets the color even if the brush is considered immutable. + private void InternalSetColor(Color value) + { + int status = Gdip.GdipSetSolidFillColor(new HandleRef(this, NativeBrush), value.ToArgb()); + Gdip.CheckStatus(status); + + _color = value; + } + + void ISystemColorTracker.OnSystemColorChanged() + { + if (NativeBrush != IntPtr.Zero) + { + InternalSetColor(_color); + } + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringAlignment.cs b/src/System.Drawing.Common/src/System/Drawing/StringAlignment.cs new file mode 100644 index 00000000000..fa21770868c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringAlignment.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the alignment of a text string relative to its layout rectangle. + /// + public enum StringAlignment + { + // left or top in English + /// + /// Specifies the text be aligned near the layout. In a left-to-right layout, the near position is left. In a + /// right-to-left layout, the near position is right. + /// + Near = 0, + + /// + /// Specifies that text is aligned in the center of the layout rectangle. + /// + Center = 1, + + // right or bottom in English + /// + /// Specifies that text is aligned far from the origin position of the layout rectangle. In a left-to-right + /// layout, the far position is right. In a right-to-left layout, the far position is left. + /// + Far = 2 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringDigitSubstitute.cs b/src/System.Drawing.Common/src/System/Drawing/StringDigitSubstitute.cs new file mode 100644 index 00000000000..44a0b21e46f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringDigitSubstitute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// font style constants (sdkinc\GDIplusEnums.h) + +namespace System.Drawing +{ + /// + /// Specifies style information applied to String Digit Substitute. + /// + public enum StringDigitSubstitute + { + User = 0, // As NLS setting + None = 1, + National = 2, + Traditional = 3 + }; +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringFormat.cs b/src/System.Drawing.Common/src/System/Drawing/StringFormat.cs new file mode 100644 index 00000000000..c22bba3ba6b --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringFormat.cs @@ -0,0 +1,442 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; +using System.ComponentModel; +using System.Drawing.Text; +using System.Runtime.InteropServices; +using System.Globalization; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// Encapsulates text layout information (such as alignment and linespacing), display manipulations (such as + /// ellipsis insertion and national digit substitution) and OpenType features. + /// + public sealed class StringFormat : MarshalByRefObject, ICloneable, IDisposable + { + internal IntPtr nativeFormat; + + private StringFormat(IntPtr format) + { + nativeFormat = format; + } + + /// + /// Initializes a new instance of the class. + /// + public StringFormat() : this(0, 0) + { + } + + /// + /// Initializes a new instance of the class with the specified . + /// + public StringFormat(StringFormatFlags options) : + this(options, 0) + { + } + + /// + /// Initializes a new instance of the class with the specified + /// and language. + /// + public StringFormat(StringFormatFlags options, int language) + { + int status = Gdip.GdipCreateStringFormat(options, language, out nativeFormat); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + /// + /// Initializes a new instance of the class from the specified + /// existing . + /// + public StringFormat(StringFormat format) + { + ArgumentNullException.ThrowIfNull(format); + + int status = Gdip.GdipCloneStringFormat(new HandleRef(format, format.nativeFormat), out nativeFormat); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + /// + /// Cleans up Windows resources for this . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (nativeFormat != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeleteStringFormat(new HandleRef(this, nativeFormat)); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) + { + if (ClientUtils.IsCriticalException(ex)) + { + throw; + } + + Debug.Fail("Exception thrown during Dispose: " + ex.ToString()); + } + finally + { + nativeFormat = IntPtr.Zero; + } + } + } + + /// + /// Creates an exact copy of this . + /// + public object Clone() + { + IntPtr cloneFormat; + + int status = Gdip.GdipCloneStringFormat(new HandleRef(this, nativeFormat), out cloneFormat); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + StringFormat newCloneStringFormat = new StringFormat(cloneFormat); + + return newCloneStringFormat; + } + + + /// + /// Gets or sets a that contains formatting information. + /// + public StringFormatFlags FormatFlags + { + get + { + StringFormatFlags format; + + int status = Gdip.GdipGetStringFormatFlags(new HandleRef(this, nativeFormat), out format); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return format; + } + set + { + int status = Gdip.GdipSetStringFormatFlags(new HandleRef(this, nativeFormat), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + /// + /// Sets the measure of characters to the specified range. + /// + public void SetMeasurableCharacterRanges(CharacterRange[] ranges) + { + int status = Gdip.GdipSetStringFormatMeasurableCharacterRanges(new HandleRef(this, nativeFormat), ranges.Length, ranges); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + // For English, this is horizontal alignment + /// + /// Specifies text alignment information. + /// + public StringAlignment Alignment + { + get + { + StringAlignment alignment; + int status = Gdip.GdipGetStringFormatAlign(new HandleRef(this, nativeFormat), out alignment); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return alignment; + } + set + { + if (value < StringAlignment.Near || value > StringAlignment.Far) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(StringAlignment)); + } + + int status = Gdip.GdipSetStringFormatAlign(new HandleRef(this, nativeFormat), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + // For English, this is vertical alignment + /// + /// Gets or sets the line alignment. + /// + public StringAlignment LineAlignment + { + get + { + StringAlignment alignment; + int status = Gdip.GdipGetStringFormatLineAlign(new HandleRef(this, nativeFormat), out alignment); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return alignment; + } + set + { + if (value < 0 || value > StringAlignment.Far) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(StringAlignment)); + } + + int status = Gdip.GdipSetStringFormatLineAlign(new HandleRef(this, nativeFormat), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + /// + /// Gets or sets the for this . + /// + public HotkeyPrefix HotkeyPrefix + { + get + { + HotkeyPrefix hotkeyPrefix; + int status = Gdip.GdipGetStringFormatHotkeyPrefix(new HandleRef(this, nativeFormat), out hotkeyPrefix); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return hotkeyPrefix; + } + set + { + if (value < HotkeyPrefix.None || value > HotkeyPrefix.Hide) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(HotkeyPrefix)); + } + + int status = Gdip.GdipSetStringFormatHotkeyPrefix(new HandleRef(this, nativeFormat), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + /// + /// Sets tab stops for this . + /// + public void SetTabStops(float firstTabOffset, float[] tabStops) + { + if (firstTabOffset < 0) + { + throw new ArgumentException(SR.Format(SR.InvalidArgumentValue, nameof(firstTabOffset), firstTabOffset)); + } + + foreach (float tabStop in tabStops) // Emulate Windows GDI+ behavior. + { + if (float.IsNegativeInfinity(tabStop)) + { + throw new NotImplementedException(); + } + } + + int status = Gdip.GdipSetStringFormatTabStops(new HandleRef(this, nativeFormat), firstTabOffset, tabStops.Length, tabStops); + + if (status != Gdip.Ok) + { + throw Gdip.StatusException(status); + } + } + + /// + /// Gets the tab stops for this . + /// + public float[] GetTabStops(out float firstTabOffset) + { + int count; + int status = Gdip.GdipGetStringFormatTabStopCount(new HandleRef(this, nativeFormat), out count); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + float[] tabStops = new float[count]; + status = Gdip.GdipGetStringFormatTabStops(new HandleRef(this, nativeFormat), count, out firstTabOffset, tabStops); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return tabStops; + } + + + // String trimming. How to handle more text than can be displayed + // in the limits available. + + /// + /// Gets or sets the for this . + /// + public StringTrimming Trimming + { + get + { + StringTrimming trimming; + int status = Gdip.GdipGetStringFormatTrimming(new HandleRef(this, nativeFormat), out trimming); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + return trimming; + } + + set + { + if (value < StringTrimming.None || value > StringTrimming.EllipsisPath) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(StringTrimming)); + } + + int status = Gdip.GdipSetStringFormatTrimming(new HandleRef(this, nativeFormat), value); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + } + + /// + /// Gets a generic default . + /// Remarks from MSDN: A generic, default StringFormat object has the following characteristics: + /// - No string format flags are set. + /// - Character alignment and line alignment are set to StringAlignmentNear. + /// - Language ID is set to neutral language, which means that the current language associated with the calling thread is used. + /// - String digit substitution is set to StringDigitSubstituteUser. + /// - Hot key prefix is set to HotkeyPrefixNone. + /// - Number of tab stops is set to zero. + /// - String trimming is set to StringTrimmingCharacter. + /// + public static StringFormat GenericDefault + { + get + { + IntPtr format; + int status = Gdip.GdipStringFormatGetGenericDefault(out format); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return new StringFormat(format); + } + } + + /// + /// Gets a generic typographic . + /// Remarks from MSDN: A generic, typographic StringFormat object has the following characteristics: + /// - String format flags StringFormatFlagsLineLimit, StringFormatFlagsNoClip, and StringFormatFlagsNoFitBlackBox are set. + /// - Character alignment and line alignment are set to StringAlignmentNear. + /// - Language ID is set to neutral language, which means that the current language associated with the calling thread is used. + /// - String digit substitution is set to StringDigitSubstituteUser. + /// - Hot key prefix is set to HotkeyPrefixNone. + /// - Number of tab stops is set to zero. + /// - String trimming is set to StringTrimmingNone. + /// + public static StringFormat GenericTypographic + { + get + { + IntPtr format; + int status = Gdip.GdipStringFormatGetGenericTypographic(out format); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return new StringFormat(format); + } + } + + public void SetDigitSubstitution(int language, StringDigitSubstitute substitute) + { + int status = Gdip.GdipSetStringFormatDigitSubstitution(new HandleRef(this, nativeFormat), language, substitute); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + } + + /// + /// Gets the for this . + /// + public StringDigitSubstitute DigitSubstitutionMethod + { + get + { + StringDigitSubstitute digitSubstitute; + + int status = Gdip.GdipGetStringFormatDigitSubstitution(new HandleRef(this, nativeFormat), out _, out digitSubstitute); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return digitSubstitute; + } + } + + /// + /// Gets the language of for this . + /// + public int DigitSubstitutionLanguage + { + get + { + int language; + int status = Gdip.GdipGetStringFormatDigitSubstitution(new HandleRef(this, nativeFormat), out language, out _); + + if (status != Gdip.Ok) + throw Gdip.StatusException(status); + + return language; + } + } + + internal int GetMeasurableCharacterRangeCount() + { + int cnt; + int status = Gdip.GdipGetStringFormatMeasurableCharacterRangeCount(new HandleRef(this, nativeFormat), out cnt); + + Gdip.CheckStatus(status); + return cnt; + } + + /// + /// Cleans up Windows resources for this . + /// + ~StringFormat() + { + Dispose(false); + } + + /// + /// Converts this to a human-readable string. + /// + public override string ToString() => $"[StringFormat, FormatFlags={FormatFlags}]"; + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringFormatFlags.cs b/src/System.Drawing.Common/src/System/Drawing/StringFormatFlags.cs new file mode 100644 index 00000000000..3ae7c17b74a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringFormatFlags.cs @@ -0,0 +1,83 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the display and layout information for text strings. + /// + [Flags] + public enum StringFormatFlags + { + /// + /// Specifies that text is right to left. + /// + DirectionRightToLeft = 0x00000001, + + /// + /// Specifies that text is vertical. + /// + DirectionVertical = 0x00000002, + + /// + /// Specifies that no part of any glyph overhangs the bounding rectangle. By default some glyphs + /// overhang the rectangle slightly where necessary to appear at the edge visually. For example + /// when an italic lower case letter f in a font such as Garamond is aligned at the far left + /// of a rectangle, the lower part of the f will reach slightly further left than the left edge + /// of the rectangle. Setting this flag will ensure no painting outside the rectangle but will + /// cause the aligned edges of adjacent lines of text to appear uneven. + /// + /// WARNING: + /// The GDI+ equivalent for this is StringFormatFlags::StringFormatFlagsNoFitBlackBox, + /// which is defined as 0x4. This was a mistake introduced since the first version of + /// the product and fixing it at this point would be a breaking change. + /// + /// + FitBlackBox = 0x00000004, + + /// + /// Causes control characters such as the left-to-right mark to be shown in the output with a representative glyph. + /// + DisplayFormatControl = 0x00000020, + + /// + /// Disables fallback to alternate fonts for characters not supported in the requested font. Any missing characters are + /// displayed with the fonts missing glyph, usually an open square. + /// + NoFontFallback = 0x00000400, + + /// + /// Specifies that the space at the end of each line is included in a string measurement. + /// + MeasureTrailingSpaces = 0x00000800, + + /// + /// Specifies that the wrapping of text to the next line is disabled. NoWrap is implied when a point of origin + /// is used instead of a layout rectangle. When drawing text within a rectangle, by default, text is broken at + /// the last word boundary that is inside the rectangle's boundary and wrapped to the next line. + /// + NoWrap = 0x00001000, + + /// + /// Specifies that only entire lines are laid out in the layout rectangle. By default, layout + /// continues until the end of the text or until no more lines are visible as a result of clipping, + /// whichever comes first. The default settings allow the last line to be partially obscured by a + /// layout rectangle that is not a whole multiple of the line height. + /// To ensure that only whole lines are seen, set this flag and be careful to provide a layout + /// rectangle at least as tall as the height of one line. + /// + LineLimit = 0x00002000, + + /// + /// Specifies that characters overhanging the layout rectangle and text extending outside the layout + /// rectangle are allowed to show. By default, all overhanging characters and text that extends outside + /// the layout rectangle are clipped. Any trailing spaces (spaces that are at the end of a line) that + /// extend outside the layout rectangle are clipped. Therefore, the setting of this flag will have an + /// effect on a string measurement if trailing spaces are being included in the measurement. + /// If clipping is enabled, trailing spaces that extend outside the layout rectangle are not included + /// in the measurement. If clipping is disabled, all trailing spaces are included in the measurement, + /// regardless of whether they are outside the layout rectangle. + /// + NoClip = 0x00004000 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringTrimming.cs b/src/System.Drawing.Common/src/System/Drawing/StringTrimming.cs new file mode 100644 index 00000000000..6d358cc3297 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringTrimming.cs @@ -0,0 +1,45 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies how to trim characters from a string that does not completely fit into a layout shape. + /// + public enum StringTrimming + { + /// + /// Specifies no trimming. + /// + None = 0, + + /// + /// Specifies that the string is broken at the boundary of the last character + /// that is inside the layout rectangle. This is the default. + /// + Character = 1, + + /// + /// Specifies that the string is broken at the boundary of the last word that is inside the layout rectangle. + /// + Word = 2, + + /// + /// Specifies that the string is broken at the boundary of the last character that is inside + /// the layout rectangle and an ellipsis (...) is inserted after the character. + /// + EllipsisCharacter = 3, + + /// + /// Specifies that the string is broken at the boundary of the last word that is inside the + /// layout rectangle and an ellipsis (...) is inserted after the word. + /// + EllipsisWord = 4, + + /// + /// Specifies that the center is removed from the string and replaced by an ellipsis. + /// The algorithm keeps as much of the last portion of the string as possible. + /// + EllipsisPath = 5 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/StringUnit.cs b/src/System.Drawing.Common/src/System/Drawing/StringUnit.cs new file mode 100644 index 00000000000..cdf90f33649 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/StringUnit.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing +{ + /// + /// Specifies the units of measure for a text string. + /// + public enum StringUnit + { + /// + /// Specifies world units as the unit of measure. + /// + World = GraphicsUnit.World, + /// + /// Specifies the device unit as the unit of measure. + /// + Display = GraphicsUnit.Display, + /// + /// Specifies a pixel as the unit of measure. + /// + Pixel = GraphicsUnit.Pixel, + /// + /// Specifies a printer's point as the unit of measure. + /// + Point = GraphicsUnit.Point, + /// + /// Specifies an inch as the unit of measure. + /// + Inch = GraphicsUnit.Inch, + /// + /// Specifies 1/300 of an inch as the unit of measure. + /// + Document = GraphicsUnit.Document, + /// + /// Specifies a millimeter as the unit of measure + /// + Millimeter = GraphicsUnit.Millimeter, + /// + /// Specifies a printer's em size of 32 as the unit of measure. + /// + Em = 32 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs b/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs new file mode 100644 index 00000000000..868531ce1b6 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SystemBrushes.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public static class SystemBrushes + { + private static readonly object s_systemBrushesKey = new object(); + + public static Brush ActiveBorder => FromSystemColor(SystemColors.ActiveBorder); + public static Brush ActiveCaption => FromSystemColor(SystemColors.ActiveCaption); + public static Brush ActiveCaptionText => FromSystemColor(SystemColors.ActiveCaptionText); + public static Brush AppWorkspace => FromSystemColor(SystemColors.AppWorkspace); + + public static Brush ButtonFace => FromSystemColor(SystemColors.ButtonFace); + public static Brush ButtonHighlight => FromSystemColor(SystemColors.ButtonHighlight); + public static Brush ButtonShadow => FromSystemColor(SystemColors.ButtonShadow); + + public static Brush Control => FromSystemColor(SystemColors.Control); + public static Brush ControlLightLight => FromSystemColor(SystemColors.ControlLightLight); + public static Brush ControlLight => FromSystemColor(SystemColors.ControlLight); + public static Brush ControlDark => FromSystemColor(SystemColors.ControlDark); + public static Brush ControlDarkDark => FromSystemColor(SystemColors.ControlDarkDark); + public static Brush ControlText => FromSystemColor(SystemColors.ControlText); + + public static Brush Desktop => FromSystemColor(SystemColors.Desktop); + + public static Brush GradientActiveCaption => FromSystemColor(SystemColors.GradientActiveCaption); + public static Brush GradientInactiveCaption => FromSystemColor(SystemColors.GradientInactiveCaption); + public static Brush GrayText => FromSystemColor(SystemColors.GrayText); + + public static Brush Highlight => FromSystemColor(SystemColors.Highlight); + public static Brush HighlightText => FromSystemColor(SystemColors.HighlightText); + public static Brush HotTrack => FromSystemColor(SystemColors.HotTrack); + + public static Brush InactiveCaption => FromSystemColor(SystemColors.InactiveCaption); + public static Brush InactiveBorder => FromSystemColor(SystemColors.InactiveBorder); + public static Brush InactiveCaptionText => FromSystemColor(SystemColors.InactiveCaptionText); + public static Brush Info => FromSystemColor(SystemColors.Info); + public static Brush InfoText => FromSystemColor(SystemColors.InfoText); + + public static Brush Menu => FromSystemColor(SystemColors.Menu); + public static Brush MenuBar => FromSystemColor(SystemColors.MenuBar); + public static Brush MenuHighlight => FromSystemColor(SystemColors.MenuHighlight); + public static Brush MenuText => FromSystemColor(SystemColors.MenuText); + + public static Brush ScrollBar => FromSystemColor(SystemColors.ScrollBar); + + public static Brush Window => FromSystemColor(SystemColors.Window); + public static Brush WindowFrame => FromSystemColor(SystemColors.WindowFrame); + public static Brush WindowText => FromSystemColor(SystemColors.WindowText); + + public static Brush FromSystemColor(Color c) + { + if (!c.IsSystemColor) + { + throw new ArgumentException(SR.Format(SR.ColorNotSystemColor, c.ToString())); + } + + Brush[]? systemBrushes = (Brush[]?)Gdip.ThreadData[s_systemBrushesKey]; + if (systemBrushes == null) + { + systemBrushes = new Brush[(int)KnownColor.WindowText + (int)KnownColor.MenuHighlight - (int)KnownColor.YellowGreen]; + Gdip.ThreadData[s_systemBrushesKey] = systemBrushes; + } + int idx = (int)c.ToKnownColor(); + if (idx > (int)KnownColor.YellowGreen) + { + idx -= (int)KnownColor.YellowGreen - (int)KnownColor.WindowText; + } + idx--; + + Debug.Assert(idx >= 0 && idx < systemBrushes.Length, "System colors have been added but our system color array has not been expanded."); + + return systemBrushes[idx] ??= new SolidBrush(c, true); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemFonts.cs b/src/System.Drawing.Common/src/System/Drawing/SystemFonts.cs new file mode 100644 index 00000000000..60476b121c4 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SystemFonts.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace System.Drawing +{ + public static class SystemFonts + { + public static Font? GetFontByName(string systemFontName) + { + if (nameof(CaptionFont).Equals(systemFontName)) + { + return CaptionFont; + } + else if (nameof(DefaultFont).Equals(systemFontName)) + { + return DefaultFont; + } + else if (nameof(DialogFont).Equals(systemFontName)) + { + return DialogFont; + } + else if (nameof(IconTitleFont).Equals(systemFontName)) + { + return IconTitleFont; + } + else if (nameof(MenuFont).Equals(systemFontName)) + { + return MenuFont; + } + else if (nameof(MessageBoxFont).Equals(systemFontName)) + { + return MessageBoxFont; + } + else if (nameof(SmallCaptionFont).Equals(systemFontName)) + { + return SmallCaptionFont; + } + else if (nameof(StatusFont).Equals(systemFontName)) + { + return StatusFont; + } + + return null; + } + + private static unsafe bool GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics) + { + metrics = new Interop.User32.NONCLIENTMETRICS { cbSize = (uint)sizeof(Interop.User32.NONCLIENTMETRICS) }; + fixed (void* m = &metrics) + { + return Interop.User32.SystemParametersInfoW(Interop.User32.SystemParametersAction.SPI_GETNONCLIENTMETRICS, metrics.cbSize, m, 0); + } + } + + public static Font? CaptionFont + { + get + { + Font? captionFont = null; + + if (GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics)) + { + captionFont = GetFontFromData(metrics.lfCaptionFont); + captionFont.SetSystemFontName(nameof(CaptionFont)); + } + + return captionFont; + } + } + + public static Font? SmallCaptionFont + { + get + { + Font? smcaptionFont = null; + + if (GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics)) + { + smcaptionFont = GetFontFromData(metrics.lfSmCaptionFont); + smcaptionFont.SetSystemFontName(nameof(SmallCaptionFont)); + } + + return smcaptionFont; + } + } + + public static Font? MenuFont + { + get + { + Font? menuFont = null; + + if (GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics)) + { + menuFont = GetFontFromData(metrics.lfMenuFont); + menuFont.SetSystemFontName(nameof(MenuFont)); + } + + return menuFont; + } + } + + public static Font? StatusFont + { + get + { + Font? statusFont = null; + + if (GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics)) + { + statusFont = GetFontFromData(metrics.lfStatusFont); + statusFont.SetSystemFontName(nameof(StatusFont)); + } + + return statusFont; + } + } + + public static Font? MessageBoxFont + { + get + { + Font? messageBoxFont = null; + + if (GetNonClientMetrics(out Interop.User32.NONCLIENTMETRICS metrics)) + { + messageBoxFont = GetFontFromData(metrics.lfMessageFont); + messageBoxFont.SetSystemFontName(nameof(MessageBoxFont)); + } + + return messageBoxFont; + } + } + + private static bool IsCriticalFontException(Exception ex) + { + return !( + // In any of these cases we'll handle the exception. + ex is ExternalException || + ex is ArgumentException || + ex is OutOfMemoryException || // GDI+ throws this one for many reasons other than actual OOM. + ex is InvalidOperationException || + ex is NotImplementedException || + ex is FileNotFoundException); + } + + public static unsafe Font? IconTitleFont + { + get + { + Font? iconTitleFont = null; + + Interop.User32.LOGFONT itfont = default; + if (Interop.User32.SystemParametersInfoW(Interop.User32.SystemParametersAction.SPI_GETICONTITLELOGFONT, (uint)sizeof(Interop.User32.LOGFONT), &itfont, 0)) + { + iconTitleFont = GetFontFromData(itfont); + iconTitleFont.SetSystemFontName(nameof(IconTitleFont)); + } + + return iconTitleFont; + } + } + + public static Font DefaultFont + { + get + { + Font? defaultFont = null; + + // For Arabic systems, always return Tahoma 8. + if ((ushort)Interop.Kernel32.GetSystemDefaultLCID() == 0x0001) + { + try + { + defaultFont = new Font("Tahoma", 8); + } + catch (Exception ex) when (!IsCriticalFontException(ex)) { } + } + + // First try DEFAULT_GUI. + if (defaultFont == null) + { + IntPtr handle = Interop.Gdi32.GetStockObject(Interop.Gdi32.StockObject.DEFAULT_GUI_FONT); + try + { + using (Font fontInWorldUnits = Font.FromHfont(handle)) + { + defaultFont = FontInPoints(fontInWorldUnits); + } + } + catch (ArgumentException) + { + // This can happen in theory if we end up pulling a non-TrueType font + } + } + + // If DEFAULT_GUI didn't work, try Tahoma. + if (defaultFont == null) + { + try + { + defaultFont = new Font("Tahoma", 8); + } + catch (ArgumentException) + { + } + } + + // Use GenericSansSerif as a last resort - this will always work. + defaultFont ??= new Font(FontFamily.GenericSansSerif, 8); + + if (defaultFont.Unit != GraphicsUnit.Point) + { + defaultFont = FontInPoints(defaultFont); + } + + Debug.Assert(defaultFont != null, "defaultFont wasn't set."); + + defaultFont.SetSystemFontName(nameof(DefaultFont)); + return defaultFont; + } + } + + public static Font DialogFont + { + get + { + Font? dialogFont = null; + + if ((ushort)Interop.Kernel32.GetSystemDefaultLCID() == 0x0011) + { + // Always return DefaultFont for Japanese cultures. + dialogFont = DefaultFont; + } + else + { + try + { + // Use MS Shell Dlg 2, 8pt for anything other than Japanese. + dialogFont = new Font("MS Shell Dlg 2", 8); + } + catch (ArgumentException) + { + // This can happen in theory if we end up pulling a non-TrueType font + } + } + + if (dialogFont == null) + { + dialogFont = DefaultFont; + } + else if (dialogFont.Unit != GraphicsUnit.Point) + { + dialogFont = FontInPoints(dialogFont); + } + + // For Japanese cultures, SystemFonts.DefaultFont returns a new Font object every time it is invoked. + // So for Japanese we return the DefaultFont with its SystemFontName set to DialogFont. + dialogFont!.SetSystemFontName(nameof(DialogFont)); + return dialogFont; + } + } + + private static Font FontInPoints(Font font) + { + return new Font(font.FontFamily, font.SizeInPoints, font.Style, GraphicsUnit.Point, font.GdiCharSet, font.GdiVerticalFont); + } + + private static Font GetFontFromData(Interop.User32.LOGFONT logFont) + { + Font? font = null; + try + { + font = Font.FromLogFont(ref logFont); + } + catch (Exception ex) when (!IsCriticalFontException(ex)) { } + + return + font == null ? DefaultFont : + font.Unit != GraphicsUnit.Point ? FontInPoints(font) : + font; + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemIcons.cs b/src/System.Drawing.Common/src/System/Drawing/SystemIcons.cs new file mode 100644 index 00000000000..8fe2f51ec9f --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SystemIcons.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; + +namespace System.Drawing +{ + public static class SystemIcons + { + private static Icon? s_application; + private static Icon? s_asterisk; + private static Icon? s_error; + private static Icon? s_exclamation; + private static Icon? s_hand; + private static Icon? s_information; + private static Icon? s_question; + private static Icon? s_warning; + private static Icon? s_winlogo; + private static Icon? s_shield; + + public static Icon Application => GetIcon(ref s_application, SafeNativeMethods.IDI_APPLICATION); + + public static Icon Asterisk => GetIcon(ref s_asterisk, SafeNativeMethods.IDI_ASTERISK); + + public static Icon Error => GetIcon(ref s_error, SafeNativeMethods.IDI_ERROR); + + public static Icon Exclamation => GetIcon(ref s_exclamation, SafeNativeMethods.IDI_EXCLAMATION); + + public static Icon Hand => GetIcon(ref s_hand, SafeNativeMethods.IDI_HAND); + + public static Icon Information => GetIcon(ref s_information, SafeNativeMethods.IDI_INFORMATION); + + public static Icon Question => GetIcon(ref s_question, SafeNativeMethods.IDI_QUESTION); + + public static Icon Warning => GetIcon(ref s_warning, SafeNativeMethods.IDI_WARNING); + + public static Icon WinLogo => GetIcon(ref s_winlogo, SafeNativeMethods.IDI_WINLOGO); + + public static Icon Shield + { + get + { + if (s_shield == null) + { + s_shield = new Icon(typeof(SystemIcons), "ShieldIcon.ico"); + Debug.Assert(s_shield != null, "ShieldIcon.ico must be present as an embedded resource in System.Drawing.Common."); + } + + return s_shield; + } + } + + private static Icon GetIcon(ref Icon? icon, int iconId) + { + return icon ??= new Icon(Interop.User32.LoadIcon(NativeMethods.NullHandleRef, (IntPtr)iconId)); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs b/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs new file mode 100644 index 00000000000..52a3dc3985c --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/SystemPens.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public static class SystemPens + { + private static readonly object s_systemPensKey = new object(); + + public static Pen ActiveBorder => FromSystemColor(SystemColors.ActiveBorder); + public static Pen ActiveCaption => FromSystemColor(SystemColors.ActiveCaption); + public static Pen ActiveCaptionText => FromSystemColor(SystemColors.ActiveCaptionText); + public static Pen AppWorkspace => FromSystemColor(SystemColors.AppWorkspace); + + public static Pen ButtonFace => FromSystemColor(SystemColors.ButtonFace); + public static Pen ButtonHighlight => FromSystemColor(SystemColors.ButtonHighlight); + + public static Pen ButtonShadow => FromSystemColor(SystemColors.ButtonShadow); + + public static Pen Control => FromSystemColor(SystemColors.Control); + public static Pen ControlText => FromSystemColor(SystemColors.ControlText); + public static Pen ControlDark => FromSystemColor(SystemColors.ControlDark); + public static Pen ControlDarkDark => FromSystemColor(SystemColors.ControlDarkDark); + public static Pen ControlLight => FromSystemColor(SystemColors.ControlLight); + public static Pen ControlLightLight => FromSystemColor(SystemColors.ControlLightLight); + + public static Pen Desktop => FromSystemColor(SystemColors.Desktop); + + public static Pen GradientActiveCaption => FromSystemColor(SystemColors.GradientActiveCaption); + public static Pen GradientInactiveCaption => FromSystemColor(SystemColors.GradientInactiveCaption); + public static Pen GrayText => FromSystemColor(SystemColors.GrayText); + + public static Pen Highlight => FromSystemColor(SystemColors.Highlight); + public static Pen HighlightText => FromSystemColor(SystemColors.HighlightText); + public static Pen HotTrack => FromSystemColor(SystemColors.HotTrack); + + public static Pen InactiveBorder => FromSystemColor(SystemColors.InactiveBorder); + public static Pen InactiveCaption => FromSystemColor(SystemColors.InactiveCaption); + public static Pen InactiveCaptionText => FromSystemColor(SystemColors.InactiveCaptionText); + public static Pen Info => FromSystemColor(SystemColors.Info); + public static Pen InfoText => FromSystemColor(SystemColors.InfoText); + + public static Pen Menu => FromSystemColor(SystemColors.Menu); + public static Pen MenuBar => FromSystemColor(SystemColors.MenuBar); + public static Pen MenuHighlight => FromSystemColor(SystemColors.MenuHighlight); + public static Pen MenuText => FromSystemColor(SystemColors.MenuText); + + public static Pen ScrollBar => FromSystemColor(SystemColors.ScrollBar); + + public static Pen Window => FromSystemColor(SystemColors.Window); + public static Pen WindowFrame => FromSystemColor(SystemColors.WindowFrame); + public static Pen WindowText => FromSystemColor(SystemColors.WindowText); + + public static Pen FromSystemColor(Color c) + { + if (!c.IsSystemColor) + { + throw new ArgumentException(SR.Format(SR.ColorNotSystemColor, c.ToString())); + } + + Pen[]? systemPens = (Pen[]?)Gdip.ThreadData[s_systemPensKey]; + if (systemPens == null) + { + systemPens = new Pen[(int)KnownColor.WindowText + (int)KnownColor.MenuHighlight - (int)KnownColor.YellowGreen]; + Gdip.ThreadData[s_systemPensKey] = systemPens; + } + + int idx = (int)c.ToKnownColor(); + if (idx > (int)KnownColor.YellowGreen) + { + idx -= (int)KnownColor.YellowGreen - (int)KnownColor.WindowText; + } + idx--; + Debug.Assert(idx >= 0 && idx < systemPens.Length, "System colors have been added but our system color array has not been expanded."); + + return systemPens[idx] ??= new Pen(c, true); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/FontCollection.cs b/src/System.Drawing.Common/src/System/Drawing/Text/FontCollection.cs new file mode 100644 index 00000000000..e774b82c205 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/FontCollection.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Text +{ + /// + /// When inherited, enumerates the FontFamily objects in a collection of fonts. + /// + public abstract class FontCollection : IDisposable + { + internal IntPtr _nativeFontCollection; + + internal FontCollection() => _nativeFontCollection = IntPtr.Zero; + + /// + /// Disposes of this + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { } + + /// + /// Gets the array of objects associated + /// with this . + /// + public FontFamily[] Families + { + get + { + int numSought; + int status = Gdip.GdipGetFontCollectionFamilyCount(new HandleRef(this, _nativeFontCollection), out numSought); + Gdip.CheckStatus(status); + + var gpfamilies = new IntPtr[numSought]; + int numFound; + status = Gdip.GdipGetFontCollectionFamilyList(new HandleRef(this, _nativeFontCollection), numSought, gpfamilies, + out numFound); + Gdip.CheckStatus(status); + + Debug.Assert(numSought == numFound, "GDI+ can't give a straight answer about how many fonts there are"); + var families = new FontFamily[numFound]; + for (int f = 0; f < numFound; f++) + { + IntPtr native; + Gdip.GdipCloneFontFamily(gpfamilies[f], out native); + families[f] = new FontFamily(native); + } + + return families; + } + } + + ~FontCollection() => Dispose(false); + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/GenericFontFamilies.cs b/src/System.Drawing.Common/src/System/Drawing/Text/GenericFontFamilies.cs new file mode 100644 index 00000000000..5b142ec1d2a --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/GenericFontFamilies.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Text +{ + /// + /// Specifies a generic . + /// + public enum GenericFontFamilies + { + /// + /// A generic Serif . + /// + Serif, + + /// + /// A generic SansSerif . + /// + SansSerif, + + /// + /// A generic Monospace . + /// + Monospace + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/HotkeyPrefix.cs b/src/System.Drawing.Common/src/System/Drawing/Text/HotkeyPrefix.cs new file mode 100644 index 00000000000..b066a36946e --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/HotkeyPrefix.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Text +{ + /// + /// Specifies the type of display for hotkey prefixes for text. + /// + public enum HotkeyPrefix + { + /// + /// No hotkey prefix. + /// + None = 0, + + /// + /// Display the hotkey prefix. + /// + Show = 1, + + /// + /// Do not display the hotkey prefix. + /// + Hide = 2 + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/InstalledFontCollection.cs b/src/System.Drawing.Common/src/System/Drawing/Text/InstalledFontCollection.cs new file mode 100644 index 00000000000..85bf4682d9d --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/InstalledFontCollection.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Text +{ + /// + /// Represents the fonts installed on the system. + /// + public sealed class InstalledFontCollection : FontCollection + { + /// + /// Initializes a new instance of the class. + /// + public InstalledFontCollection() : base() + { + int status = Gdip.GdipNewInstalledFontCollection(out _nativeFontCollection); + Gdip.CheckStatus(status); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs b/src/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs new file mode 100644 index 00000000000..6d61b4e5af5 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/PrivateFontCollection.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Globalization; +using System.IO; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing.Text +{ + /// + /// Encapsulates a collection of objects. + /// + public sealed class PrivateFontCollection : FontCollection + { + /// + /// Initializes a new instance of the class. + /// + public PrivateFontCollection() : base() + { + int status = Gdip.GdipNewPrivateFontCollection(out _nativeFontCollection); + Gdip.CheckStatus(status); + } + + /// + /// Cleans up Windows resources for this . + /// + protected override void Dispose(bool disposing) + { + if (_nativeFontCollection != IntPtr.Zero) + { + try + { +#if DEBUG + int status = !Gdip.Initialized ? Gdip.Ok : +#endif + Gdip.GdipDeletePrivateFontCollection(ref _nativeFontCollection); +#if DEBUG + Debug.Assert(status == Gdip.Ok, $"GDI+ returned an error status: {status.ToString(CultureInfo.InvariantCulture)}"); +#endif + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + } + finally + { + _nativeFontCollection = IntPtr.Zero; + } + } + + base.Dispose(disposing); + } + + /// + /// Adds a font from the specified file to this . + /// + public void AddFontFile(string filename) + { + if (_nativeFontCollection == IntPtr.Zero) + { +#pragma warning disable CA2208 // Instantiate argument exceptions correctly + // This is the default behavior on Desktop. The ArgumentException originates from GdipPrivateAddFontFile which would + // refuse the null pointer. + throw new ArgumentException(); +#pragma warning restore CA2208 + } + + if (filename == null) + { + throw new ArgumentNullException(nameof(filename)); + } + + // this ensure the filename is valid (or throw the correct exception) + string fullPath = Path.GetFullPath(filename); + + if (!File.Exists(fullPath)) + { + throw new FileNotFoundException(); + } + + int status = Gdip.GdipPrivateAddFontFile(new HandleRef(this, _nativeFontCollection), fullPath); + Gdip.CheckStatus(status); + + // Register private font with GDI as well so pure GDI-based controls (TextBox, Button for instance) can access it. + GdiAddFontFile(filename); + } + + /// + /// Adds a font contained in system memory to this . + /// + public void AddMemoryFont(IntPtr memory, int length) + { + Gdip.CheckStatus(Gdip.GdipPrivateAddMemoryFont(new HandleRef(this, _nativeFontCollection), memory, length)); + } + + private static void GdiAddFontFile(string filename) + { + Interop.Gdi32.AddFontFile(filename); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/Text/TextRenderingHint.cs b/src/System.Drawing.Common/src/System/Drawing/Text/TextRenderingHint.cs new file mode 100644 index 00000000000..5f3b55934ed --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/Text/TextRenderingHint.cs @@ -0,0 +1,41 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Text +{ + /// + /// Specifies the quality of text rendering. + /// + public enum TextRenderingHint + { + /// + /// Glyph with system default rendering hint. + /// + SystemDefault = 0, + + /// + /// Glyph bitmap with hinting. + /// + SingleBitPerPixelGridFit, + + /// + /// Glyph bitmap without hinting. + /// + SingleBitPerPixel, + + /// + /// Anti-aliasing with hinting. + /// + AntiAliasGridFit, + + /// + /// Glyph anti-alias bitmap without hinting. + /// + AntiAlias, + + /// + /// Glyph CT bitmap with hinting. + /// + ClearTypeGridFit + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/TextureBrush.cs b/src/System.Drawing.Common/src/System/Drawing/TextureBrush.cs new file mode 100644 index 00000000000..4f7755ee425 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/TextureBrush.cs @@ -0,0 +1,255 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.ComponentModel; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + public sealed class TextureBrush : Brush + { + // When creating a texture brush from a metafile image, the dstRect + // is used to specify the size that the metafile image should be + // rendered at in the device units of the destination graphics. + // It is NOT used to crop the metafile image, so only the width + // and height values matter for metafiles. + + public TextureBrush(Image bitmap) : this(bitmap, WrapMode.Tile) + { + } + + public TextureBrush(Image image, WrapMode wrapMode) + { + ArgumentNullException.ThrowIfNull(image); + + if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp) + { + throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode)); + } + + IntPtr brush; + int status = Gdip.GdipCreateTexture(new HandleRef(image, image.nativeImage), + (int)wrapMode, + out brush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(brush); + } + + public TextureBrush(Image image, WrapMode wrapMode, RectangleF dstRect) + { + ArgumentNullException.ThrowIfNull(image); + + if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp) + { + throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode)); + } + + IntPtr brush; + int status = Gdip.GdipCreateTexture2(new HandleRef(image, image.nativeImage), + unchecked((int)wrapMode), + dstRect.X, + dstRect.Y, + dstRect.Width, + dstRect.Height, + out brush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(brush); + } + + public TextureBrush(Image image, WrapMode wrapMode, Rectangle dstRect) + { + ArgumentNullException.ThrowIfNull(image); + + if (wrapMode < WrapMode.Tile || wrapMode > WrapMode.Clamp) + { + throw new InvalidEnumArgumentException(nameof(wrapMode), unchecked((int)wrapMode), typeof(WrapMode)); + } + + IntPtr brush; + int status = Gdip.GdipCreateTexture2I(new HandleRef(image, image.nativeImage), + unchecked((int)wrapMode), + dstRect.X, + dstRect.Y, + dstRect.Width, + dstRect.Height, + out brush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(brush); + } + + public TextureBrush(Image image, RectangleF dstRect) : this(image, dstRect, null) { } + + public TextureBrush(Image image, RectangleF dstRect, ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(image); + + IntPtr brush; + int status = Gdip.GdipCreateTextureIA(new HandleRef(image, image.nativeImage), + new HandleRef(imageAttr, (imageAttr == null) ? + IntPtr.Zero : imageAttr.nativeImageAttributes), + dstRect.X, + dstRect.Y, + dstRect.Width, + dstRect.Height, + out brush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(brush); + } + + public TextureBrush(Image image, Rectangle dstRect) : this(image, dstRect, null) { } + + public TextureBrush(Image image, Rectangle dstRect, ImageAttributes? imageAttr) + { + ArgumentNullException.ThrowIfNull(image); + + IntPtr brush; + int status = Gdip.GdipCreateTextureIAI(new HandleRef(image, image.nativeImage), + new HandleRef(imageAttr, (imageAttr == null) ? + IntPtr.Zero : imageAttr.nativeImageAttributes), + dstRect.X, + dstRect.Y, + dstRect.Width, + dstRect.Height, + out brush); + Gdip.CheckStatus(status); + + SetNativeBrushInternal(brush); + } + + internal TextureBrush(IntPtr nativeBrush) + { + Debug.Assert(nativeBrush != IntPtr.Zero, "Initializing native brush with null."); + SetNativeBrushInternal(nativeBrush); + } + + public override object Clone() + { + IntPtr cloneBrush; + int status = Gdip.GdipCloneBrush(new HandleRef(this, NativeBrush), out cloneBrush); + Gdip.CheckStatus(status); + + return new TextureBrush(cloneBrush); + } + + public Matrix Transform + { + get + { + var matrix = new Matrix(); + int status = Gdip.GdipGetTextureTransform(new HandleRef(this, NativeBrush), new HandleRef(matrix, matrix.NativeMatrix)); + Gdip.CheckStatus(status); + + return matrix; + } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + int status = Gdip.GdipSetTextureTransform(new HandleRef(this, NativeBrush), new HandleRef(value, value.NativeMatrix)); + Gdip.CheckStatus(status); + } + } + + public WrapMode WrapMode + { + get + { + int mode; + int status = Gdip.GdipGetTextureWrapMode(new HandleRef(this, NativeBrush), out mode); + Gdip.CheckStatus(status); + + return (WrapMode)mode; + } + set + { + if (value < WrapMode.Tile || value > WrapMode.Clamp) + { + throw new InvalidEnumArgumentException(nameof(value), unchecked((int)value), typeof(WrapMode)); + } + + int status = Gdip.GdipSetTextureWrapMode(new HandleRef(this, NativeBrush), unchecked((int)value)); + Gdip.CheckStatus(status); + } + } + + public Image Image + { + get + { + IntPtr image; + int status = Gdip.GdipGetTextureImage(new HandleRef(this, NativeBrush), out image); + Gdip.CheckStatus(status); + + return Image.CreateImageObject(image); + } + } + + public void ResetTransform() + { + int status = Gdip.GdipResetTextureTransform(new HandleRef(this, NativeBrush)); + Gdip.CheckStatus(status); + } + + public void MultiplyTransform(Matrix matrix) => MultiplyTransform(matrix, MatrixOrder.Prepend); + + public void MultiplyTransform(Matrix matrix, MatrixOrder order) + { + ArgumentNullException.ThrowIfNull(matrix); + + // Multiplying the transform by a disposed matrix is a nop in GDI+, but throws + // with the libgdiplus backend. Simulate a nop for compatibility with GDI+. + if (matrix.NativeMatrix == IntPtr.Zero) + { + return; + } + + int status = Gdip.GdipMultiplyTextureTransform(new HandleRef(this, NativeBrush), + new HandleRef(matrix, matrix.NativeMatrix), + order); + Gdip.CheckStatus(status); + } + + public void TranslateTransform(float dx, float dy) => TranslateTransform(dx, dy, MatrixOrder.Prepend); + + public void TranslateTransform(float dx, float dy, MatrixOrder order) + { + int status = Gdip.GdipTranslateTextureTransform(new HandleRef(this, NativeBrush), + dx, + dy, + order); + Gdip.CheckStatus(status); + } + + public void ScaleTransform(float sx, float sy) => ScaleTransform(sx, sy, MatrixOrder.Prepend); + + public void ScaleTransform(float sx, float sy, MatrixOrder order) + { + int status = Gdip.GdipScaleTextureTransform(new HandleRef(this, NativeBrush), + sx, + sy, + order); + Gdip.CheckStatus(status); + } + + public void RotateTransform(float angle) => RotateTransform(angle, MatrixOrder.Prepend); + + public void RotateTransform(float angle, MatrixOrder order) + { + int status = Gdip.GdipRotateTextureTransform(new HandleRef(this, NativeBrush), + angle, + order); + Gdip.CheckStatus(status); + } + } +} diff --git a/src/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs b/src/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs new file mode 100644 index 00000000000..51206af7353 --- /dev/null +++ b/src/System.Drawing.Common/src/System/Drawing/ToolboxBitmapAttribute.cs @@ -0,0 +1,332 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing.Imaging; +using System.IO; +using DpiHelper = System.Windows.Forms.DpiHelper; +using Gdip = System.Drawing.SafeNativeMethods.Gdip; + +namespace System.Drawing +{ + /// + /// ToolboxBitmapAttribute defines the images associated with a specified component. + /// The component can offer a small and large image (large is optional). + /// + [AttributeUsage(AttributeTargets.Class)] + public class ToolboxBitmapAttribute : Attribute + { + private Image? _smallImage; + private Image? _largeImage; + + private readonly string? _imageFile; + private readonly Type? _imageType; + + private readonly string? _imageName; + + private static readonly Size s_largeSize = new Size(32, 32); + private static readonly Size s_smallSize = new Size(16, 16); + + public ToolboxBitmapAttribute(string imageFile) : this(GetImageFromFile(imageFile, false), GetImageFromFile(imageFile, true)) + { + _imageFile = imageFile; + } + + public ToolboxBitmapAttribute(Type t) : this(GetImageFromResource(t, null, false), GetImageFromResource(t, null, true)) + { + _imageType = t; + } + + public ToolboxBitmapAttribute(Type t, string name) + : this(GetImageFromResource(t, name, false), GetImageFromResource(t, name, true)) + { + _imageType = t; + _imageName = name; + } + + private ToolboxBitmapAttribute(Image? smallImage, Image? largeImage) + { + _smallImage = smallImage; + _largeImage = largeImage; + } + + public override bool Equals([NotNullWhen(true)] object? value) + { + if (value == this) + { + return true; + } + + if (value is ToolboxBitmapAttribute attr) + { + return attr._smallImage == _smallImage && attr._largeImage == _largeImage; + } + + return false; + } + + public override int GetHashCode() => base.GetHashCode(); + + public Image? GetImage(object? component) => GetImage(component, true); + + public Image? GetImage(object? component, bool large) + { + if (component != null) + { + return GetImage(component.GetType(), large); + } + + return null; + } + + public Image? GetImage(Type type) => GetImage(type, false); + + public Image? GetImage(Type type, bool large) => GetImage(type, null, large); + + public Image? GetImage(Type type, string? imgName, bool large) + { + if ((large && _largeImage == null) || (!large && _smallImage == null)) + { + Image? img = large ? _largeImage : _smallImage; + img ??= GetImageFromResource(type, imgName, large); + + // last resort for large images. + if (large && _largeImage == null && _smallImage != null) + { + img = new Bitmap((Bitmap)_smallImage, s_largeSize.Width, s_largeSize.Height); + } + + if (img is Bitmap b) + { + MakeBackgroundAlphaZero(b); + } + + if (img == null) + { + img = s_defaultComponent.GetImage(type, large); + + // We don't want to hand out the static shared image + // because otherwise it might get disposed. + if (img != null) + { + img = (Image)img.Clone(); + } + } + + if (large) + { + _largeImage = img; + } + else + { + _smallImage = img; + } + } + + Image? toReturn = (large) ? _largeImage : _smallImage; + + if (Equals(Default)) + { + _largeImage = null; + _smallImage = null; + } + + return toReturn; + } + + // Helper to get the right icon from the given stream that represents an icon. + private static Bitmap? GetIconFromStream(Stream? stream, bool large, bool scaled) + { + if (stream == null) + { + return null; + } + Icon ico = new Icon(stream); + Icon sizedico = new Icon(ico, large ? s_largeSize : s_smallSize); + Bitmap? b = sizedico.ToBitmap(); + if (DpiHelper.IsScalingRequired && scaled) + { + DpiHelper.ScaleBitmapLogicalToDevice(ref b); + } + return b; + } + + // Just forwards to Image.FromFile eating any non-critical exceptions that may result. + private static Image? GetImageFromFile(string? imageFile, bool large, bool scaled = true) + { + Image? image = null; + try + { + if (imageFile != null) + { + string? ext = Path.GetExtension(imageFile); + if (ext != null && string.Equals(ext, ".ico", StringComparison.OrdinalIgnoreCase)) + { + //ico files support both large and small, so we respect the large flag here. + using (FileStream reader = File.OpenRead(imageFile!)) + { + image = GetIconFromStream(reader, large, scaled); + } + } + else if (!large) + { + //we only read small from non-ico files. + image = Image.FromFile(imageFile!); + Bitmap? b = image as Bitmap; + if (DpiHelper.IsScalingRequired && scaled) + { + DpiHelper.ScaleBitmapLogicalToDevice(ref b); + } + } + } + } + catch (Exception e) when (!ClientUtils.IsCriticalException(e)) + { + } + + return image; + } + + private static Image? GetBitmapFromResource(Type t, string? bitmapname, bool large, bool scaled) + { + if (bitmapname == null) + { + return null; + } + + Image? img = null; + + // Load the image from the manifest resources. + Stream? stream = BitmapSelector.GetResourceStream(t, bitmapname); + if (stream != null) + { + Bitmap? b = new Bitmap(stream); + img = b; + MakeBackgroundAlphaZero(b); + if (large) + { + img = new Bitmap(b, s_largeSize.Width, s_largeSize.Height); + } + if (DpiHelper.IsScalingRequired && scaled) + { + b = (Bitmap)img; + DpiHelper.ScaleBitmapLogicalToDevice(ref b); + img = b; + } + } + return img; + } + + private static Bitmap? GetIconFromResource(Type t, string? bitmapname, bool large, bool scaled) + { + if (bitmapname == null) + { + return null; + } + + return GetIconFromStream(BitmapSelector.GetResourceStream(t, bitmapname), large, scaled); + } + + public static Image? GetImageFromResource(Type t, string? imageName, bool large) + { + return GetImageFromResource(t, imageName, large, scaled: true); + } + + internal static Image? GetImageFromResource(Type t, string? imageName, bool large, bool scaled) + { + Image? img = null; + try + { + string? name = imageName; + string? iconname = null; + string? bmpname = null; + string? rawbmpname = null; + + // If we didn't get a name, use the class name + if (name == null) + { + name = t.FullName!; + int indexDot = name.LastIndexOf('.'); + if (indexDot != -1) + { + name = name.Substring(indexDot + 1); + } + + // All bitmap images from winforms runtime are changed to Icons + // and logical names, now, does not contain any extension. + rawbmpname = name; + iconname = name + ".ico"; + bmpname = name + ".bmp"; + } + else + { + if (string.Equals(Path.GetExtension(imageName), ".ico", StringComparison.CurrentCultureIgnoreCase)) + { + iconname = name; + } + else if (string.Equals(Path.GetExtension(imageName), ".bmp", StringComparison.CurrentCultureIgnoreCase)) + { + bmpname = name; + } + else + { + // We don't recognize the name as either bmp or ico. we need to try three things. + // 1. the name as a bitmap (back compat) + // 2. name+.bmp + // 3. name+.ico + rawbmpname = name; + bmpname = name + ".bmp"; + iconname = name + ".ico"; + } + } + if (rawbmpname != null) + { + img = GetBitmapFromResource(t, rawbmpname, large, scaled); + } + if (img == null && bmpname != null) + { + img = GetBitmapFromResource(t, bmpname, large, scaled); + } + if (img == null && iconname != null) + { + img = GetIconFromResource(t, iconname, large, scaled); + } + } + catch (Exception) { } + return img; + } + + private static void MakeBackgroundAlphaZero(Bitmap img) + { + // Bitmap derived from Icon is already transparent. + if (img.RawFormat.Guid == ImageFormat.Icon.Guid) + return; + + Color bottomLeft = img.GetPixel(0, img.Height - 1); + img.MakeTransparent(); + + Color newBottomLeft = Color.FromArgb(0, bottomLeft); + img.SetPixel(0, img.Height - 1, newBottomLeft); + } + + public static readonly ToolboxBitmapAttribute Default = new ToolboxBitmapAttribute(null, (Image?)null); + + private static readonly ToolboxBitmapAttribute s_defaultComponent; + +#pragma warning disable CA1810 // DummyFunction apparently needs to be invoked prior to the rest of the initialization + static ToolboxBitmapAttribute() + { + // When we call Gdip.DummyFunction, JIT will make sure Gdip..cctor will be called. + Gdip.DummyFunction(); + + Stream? stream = BitmapSelector.GetResourceStream(typeof(ToolboxBitmapAttribute), "DefaultComponent.bmp"); + Debug.Assert(stream != null, "DefaultComponent.bmp must be present as an embedded resource."); + + var bitmap = new Bitmap(stream); + MakeBackgroundAlphaZero(bitmap); + s_defaultComponent = new ToolboxBitmapAttribute(bitmap, null); + } +#pragma warning restore CA1810 + } +} diff --git a/src/System.Drawing.Common/src/_._ b/src/System.Drawing.Common/src/_._ new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/System.Drawing.Common/src/misc/DbgUtil.cs b/src/System.Drawing.Common/src/misc/DbgUtil.cs new file mode 100644 index 00000000000..7fcbc938436 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/DbgUtil.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; + +namespace System.Drawing.Internal +{ + internal sealed class DbgUtil + { + /// + /// Call this method from your Dispose(bool) to assert that unmanaged resources has been explicitly disposed. + /// + [Conditional("DEBUG")] + public static void AssertFinalization(object obj, bool disposing) + { +#if GDI_FINALIZATION_WATCH + if ( disposing || AppDomain.CurrentDomain.IsFinalizingForUnload() ) + { + return; + } + + try + { + BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Static | BindingFlags.Instance; + FieldInfo allocSiteFld = obj.GetType().GetField("AllocationSite", bindingFlags); + string allocationSite = allocSiteFld != null ? allocSiteFld.GetValue( obj ).ToString() : ""; + + // ignore ojects created by WindowsGraphicsCacheManager. + if ( allocationSite.Contains("WindowsGraphicsCacheManager") ) + { + return; + } + + Debug.Fail("Object Disposed through finalization - it should be explicitly disposed."); + Debug.WriteLine("Allocation stack:\r\n" + allocationSite); + } + catch (Exception ex) + { + try + { + Debug.WriteLine("Exception thrown while trying to get allocation stack: " + ex); + } + catch + { + } + } +#endif + } + + [Conditional("DEBUG")] + public static void AssertWin32(bool expression, string format, params object[] args) + { + if (!expression) + { + var e = new Win32Exception(); + string message = string.Format(CultureInfo.CurrentCulture, format, args); + Debug.Fail($"{message}\r\nError: 0x{e.NativeErrorCode:x8} - {e.Message}"); + } + } + } +} diff --git a/src/System.Drawing.Common/src/misc/DpiHelper.cs b/src/System.Drawing.Common/src/misc/DpiHelper.cs new file mode 100644 index 00000000000..20c441a2df5 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/DpiHelper.cs @@ -0,0 +1,229 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Configuration; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.InteropServices; + +namespace System.Windows.Forms +{ + /// + /// Helper class for scaling coordinates and images according to current DPI scaling set in Windows for the primary screen. + /// + internal static class DpiHelper + { + private const double LogicalDpi = 96.0; + private static bool s_isInitialized; + /// + /// The primary screen's (device) current horizontal DPI + /// + private static double s_deviceDpiX = LogicalDpi; + + /// + /// The primary screen's (device) current vertical DPI + /// + private static double s_deviceDpiY = LogicalDpi; + + private static double s_logicalToDeviceUnitsScalingFactorX; + private static double s_logicalToDeviceUnitsScalingFactorY; + private static InterpolationMode s_interpolationMode = InterpolationMode.Invalid; + + private static void Initialize() + { + if (s_isInitialized) + { + return; + } + + IntPtr hDC = Interop.User32.GetDC(IntPtr.Zero); + if (hDC != IntPtr.Zero) + { + s_deviceDpiX = Interop.Gdi32.GetDeviceCaps(hDC, Interop.Gdi32.DeviceCapability.LOGPIXELSX); + s_deviceDpiY = Interop.Gdi32.GetDeviceCaps(hDC, Interop.Gdi32.DeviceCapability.LOGPIXELSY); + + Interop.User32.ReleaseDC(IntPtr.Zero, hDC); + } + + s_isInitialized = true; + } + + private static double LogicalToDeviceUnitsScalingFactorX + { + get + { + if (s_logicalToDeviceUnitsScalingFactorX == 0.0) + { + Initialize(); + s_logicalToDeviceUnitsScalingFactorX = s_deviceDpiX / LogicalDpi; + } + + return s_logicalToDeviceUnitsScalingFactorX; + } + } + + private static double LogicalToDeviceUnitsScalingFactorY + { + get + { + if (s_logicalToDeviceUnitsScalingFactorY == 0.0) + { + Initialize(); + s_logicalToDeviceUnitsScalingFactorY = s_deviceDpiY / LogicalDpi; + } + + return s_logicalToDeviceUnitsScalingFactorY; + } + } + + private static InterpolationMode InterpolationMode + { + get + { + if (s_interpolationMode == InterpolationMode.Invalid) + { + int dpiScalePercent = (int)Math.Round(LogicalToDeviceUnitsScalingFactorX * 100); + + // We will prefer NearestNeighbor algorithm for 200, 300, 400, etc zoom factors, in which each pixel become a 2x2, 3x3, 4x4, etc rectangle. + // This produces sharp edges in the scaled image and doesn't cause distorsions of the original image. + // For any other scale factors we will prefer a high quality resizing algorithm. While that introduces fuzziness in the resulting image, + // it will not distort the original (which is extremely important for small zoom factors like 125%, 150%). + // We'll use Bicubic in those cases, except on reducing (zoom < 100, which we shouldn't have anyway), in which case Linear produces better + // results because it uses less neighboring pixels. + if ((dpiScalePercent % 100) == 0) + { + s_interpolationMode = InterpolationMode.NearestNeighbor; + } + else if (dpiScalePercent < 100) + { + s_interpolationMode = InterpolationMode.HighQualityBilinear; + } + else + { + s_interpolationMode = InterpolationMode.HighQualityBicubic; + } + } + return s_interpolationMode; + } + } + + private static Bitmap ScaleBitmapToSize(Bitmap logicalImage, Size deviceImageSize) + { + Bitmap deviceImage = new Bitmap(deviceImageSize.Width, deviceImageSize.Height, logicalImage.PixelFormat); + + using (Graphics graphics = Graphics.FromImage(deviceImage)) + { + graphics.InterpolationMode = InterpolationMode; + + RectangleF sourceRect = new RectangleF(0, 0, logicalImage.Size.Width, logicalImage.Size.Height); + RectangleF destRect = new RectangleF(0, 0, deviceImageSize.Width, deviceImageSize.Height); + + // Specify a source rectangle shifted by half of pixel to account for GDI+ considering the source origin the center of top-left pixel + // Failing to do so will result in the right and bottom of the bitmap lines being interpolated with the graphics' background color, + // and will appear black even if we cleared the background with transparent color. + // The apparition of these artifacts depends on the interpolation mode, on the dpi scaling factor, etc. + // E.g. at 150% DPI, Bicubic produces them and NearestNeighbor is fine, but at 200% DPI NearestNeighbor also shows them. + sourceRect.Offset(-0.5f, -0.5f); + + graphics.DrawImage(logicalImage, destRect, sourceRect, GraphicsUnit.Pixel); + } + + return deviceImage; + } + + private static Bitmap CreateScaledBitmap(Bitmap logicalImage) + { + Size deviceImageSize = LogicalToDeviceUnits(logicalImage.Size); + return ScaleBitmapToSize(logicalImage, deviceImageSize); + } + + /// + /// Returns whether scaling is required when converting between logical-device units, + /// if the application opted in the automatic scaling in the .config file. + /// + public static bool IsScalingRequired + { + get + { + Initialize(); + return s_deviceDpiX != LogicalDpi || s_deviceDpiY != LogicalDpi; + } + } + + /// + /// Transforms a horizontal integer coordinate from logical to device units + /// by scaling it up for current DPI and rounding to nearest integer value + /// Note: this method should be called only inside an if (DpiHelper.IsScalingRequired) clause + /// + /// The horizontal value in logical units + /// The horizontal value in device units + public static int LogicalToDeviceUnitsX(int value) + { + return (int)Math.Round(LogicalToDeviceUnitsScalingFactorX * (double)value); + } + + /// + /// Transforms a vertical integer coordinate from logical to device units + /// by scaling it up for current DPI and rounding to nearest integer value + /// Note: this method should be called only inside an if (DpiHelper.IsScalingRequired) clause + /// + /// The vertical value in logical units + /// The vertical value in device units + public static int LogicalToDeviceUnitsY(int value) + { + return (int)Math.Round(LogicalToDeviceUnitsScalingFactorY * (double)value); + } + + /// + /// Returns a new Size with the input's + /// dimensions converted from logical units to device units. + /// Note: this method should be called only inside an if (DpiHelper.IsScalingRequired) clause + /// + /// Size in logical units + /// Size in device units + public static Size LogicalToDeviceUnits(Size logicalSize) + { + return new Size(LogicalToDeviceUnitsX(logicalSize.Width), + LogicalToDeviceUnitsY(logicalSize.Height)); + } + + /// + /// Create and return a new bitmap scaled to the specified size. + /// Note: this method should be called only inside an if (DpiHelper.IsScalingRequired) clause + /// + /// The image to scale from logical units to device units + /// The size to scale image to + [return: NotNullIfNotNull(nameof(logicalImage))] + public static Bitmap? CreateResizedBitmap(Bitmap? logicalImage, Size targetImageSize) + { + if (logicalImage == null) + { + return null; + } + + return ScaleBitmapToSize(logicalImage, targetImageSize); + } + + /// + /// Create a new bitmap scaled for the device units. + /// When displayed on the device, the scaled image will have same size as the original image would have when displayed at 96dpi. + /// Note: this method should be called only inside an if (DpiHelper.IsScalingRequired) clause + /// + /// The image to scale from logical units to device units + public static void ScaleBitmapLogicalToDevice([NotNullIfNotNull(nameof(logicalBitmap))]ref Bitmap? logicalBitmap) + { + if (logicalBitmap == null) + { + return; + } + + Bitmap deviceBitmap = CreateScaledBitmap(logicalBitmap); + if (deviceBitmap != null) + { + logicalBitmap.Dispose(); + logicalBitmap = deviceBitmap; + } + } + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/ApplyGraphicsProperties.cs b/src/System.Drawing.Common/src/misc/GDI/ApplyGraphicsProperties.cs new file mode 100644 index 00000000000..0be6abb9569 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/ApplyGraphicsProperties.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Internal +{ + /// + /// Enumeration defining the different Graphics properties to apply to a WindowsGraphics when creating it from a + /// Graphics object. + /// + [Flags] + internal enum ApplyGraphicsProperties + { + // No properties to be applied to the DC obtained from the Graphics object. + None = 0x00000000, + // Apply clipping region. + Clipping = 0x00000001, + // Apply coordinate transformation. + TranslateTransform = 0x00000002, + // Apply all supported Graphics properties. + All = Clipping | TranslateTransform + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/DeviceContext.cs b/src/System.Drawing.Common/src/misc/GDI/DeviceContext.cs new file mode 100644 index 00000000000..707e0ac8512 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/DeviceContext.cs @@ -0,0 +1,350 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + /// + /// Represents a Win32 device context. Provides operations for setting some of the properties of a device context. + /// It's the managed wrapper for an HDC. + /// + /// This class is divided into two files separating the code that needs to be compiled into retail builds and + /// debugging code. + /// + internal sealed partial class DeviceContext : MarshalByRefObject, IDisposable + { + /// + /// This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a + /// handle to it. + /// + /// The hDc is released/deleted only when owned by the object, meaning it was created internally; + /// in this case, the object is responsible for releasing/deleting it. + /// In the case the object is created from an existing hdc, it is not released; this is consistent + /// with the Win32 guideline that says if you call GetDC/CreateDC/CreatIC/CreateEnhMetafile, you are + /// responsible for calling ReleaseDC/DeleteDC/DeleteEnhMetafile respectively. + /// + /// This class implements some of the operations commonly performed on the properties of a dc in WinForms, + /// specially for interacting with GDI+, like clipping and coordinate transformation. + /// Several properties are not persisted in the dc but instead they are set/reset during a more comprehensive + /// operation like text rendering or painting; for instance text alignment is set and reset during DrawText (GDI), + /// DrawString (GDI+). + /// + /// Other properties are persisted from operation to operation until they are reset, like clipping, + /// one can make several calls to Graphics or WindowsGraphics object after setting the dc clip area and + /// before resetting it; these kinds of properties are the ones implemented in this class. + /// This kind of properties place an extra challenge in the scenario where a DeviceContext is obtained + /// from a Graphics object that has been used with GDI+, because GDI+ saves the hdc internally, rendering the + /// DeviceContext underlying hdc out of sync. DeviceContext needs to support these kind of properties to + /// be able to keep the GDI+ and GDI HDCs in sync. + /// + /// A few other persisting properties have been implemented in DeviceContext2, among them: + /// 1. Window origin. + /// 2. Bounding rectangle. + /// 3. DC origin. + /// 4. View port extent. + /// 5. View port origin. + /// 6. Window extent + /// + /// Other non-persisted properties just for information: Background/Foreground color, Palette, Color adjustment, + /// Color space, ICM mode and profile, Current pen position, Binary raster op (not supported by GDI+), + /// Background mode, Logical Pen, DC pen color, ARc direction, Miter limit, Logical brush, DC brush color, + /// Brush origin, Polygon filling mode, Bitmap stretching mode, Logical font, Intercharacter spacing, + /// Font mapper flags, Text alignment, Test justification, Layout, Path, Meta region. + /// See book "Windows Graphics Programming - Feng Yuang", P315 - Device Context Attributes. + /// + + private IntPtr _hDC; + private readonly DeviceContextType _dcType; + + public event EventHandler? Disposing; + + private bool _disposed; + + private IntPtr _hInitialPen; + private IntPtr _hInitialBrush; + private IntPtr _hInitialBmp; + private IntPtr _hInitialFont; + + private IntPtr _hCurrentPen; + private IntPtr _hCurrentBrush; + private IntPtr _hCurrentBmp; + private IntPtr _hCurrentFont; + + private Stack? _contextStack; + +#if GDI_FINALIZATION_WATCH + private string AllocationSite = DbgUtil.StackTrace; + private string DeAllocationSite = ""; +#endif + + /// + /// This object's hdc. If this property is called, then the object will be used as an HDC wrapper, so the hdc + /// is cached and calls to GetHdc/ReleaseHdc won't PInvoke into GDI. Call Dispose to properly release the hdc. + /// + public IntPtr Hdc => _hDC; + + // Due to a problem with calling DeleteObject() on currently selected GDI objects, we now track the initial set + // of objects when a DeviceContext is created. Then, we also track which objects are currently selected in the + // DeviceContext. When a currently selected object is disposed, it is first replaced in the DC and then deleted. + private void CacheInitialState() + { + Debug.Assert(_hDC != IntPtr.Zero, "Cannot get initial state without a valid HDC"); + _hCurrentPen = _hInitialPen = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_PEN); + _hCurrentBrush = _hInitialBrush = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_BRUSH); + _hCurrentBmp = _hInitialBmp = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_BITMAP); + _hCurrentFont = _hInitialFont = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_FONT); + } + + /// + /// Constructor to construct a DeviceContext object from an existing Win32 device context handle. + /// + private DeviceContext(IntPtr hDC, DeviceContextType dcType) + { + _hDC = hDC; + _dcType = dcType; + + CacheInitialState(); + DeviceContexts.AddDeviceContext(this); + +#if TRACK_HDC + Debug.WriteLine(DbgUtil.StackTraceToStr($"DeviceContext(hDC=0x{(int)hDC:X8}, Type={dcType})")); +#endif + } + + /// + /// CreateDC creates a DeviceContext object wrapping an hdc created with the Win32 CreateDC function. + /// + public static DeviceContext CreateDC(string driverName, string deviceName, string? fileName, IntPtr devMode) + { + // Note: All input params can be null but not at the same time. See MSDN for information. + IntPtr hdc = Interop.Gdi32.CreateDCW(driverName, deviceName, fileName, devMode); + return new DeviceContext(hdc, DeviceContextType.NamedDevice); + } + + /// + /// CreateIC creates a DeviceContext object wrapping an hdc created with the Win32 CreateIC function. + /// + public static DeviceContext CreateIC(string driverName, string deviceName, string? fileName, IntPtr devMode) + { + // Note: All input params can be null but not at the same time. See MSDN for information. + + IntPtr hdc = Interop.Gdi32.CreateICW(driverName, deviceName, fileName, devMode); + return new DeviceContext(hdc, DeviceContextType.Information); + } + + /// + /// Used for wrapping an existing hdc. In this case, this object doesn't own the hdc so calls to + /// GetHdc/ReleaseHdc don't PInvoke into GDI. + /// + public static DeviceContext FromHdc(IntPtr hdc) + { + Debug.Assert(hdc != IntPtr.Zero, "hdc == 0"); + return new DeviceContext(hdc, DeviceContextType.Unknown); + } + + ~DeviceContext() => Dispose(false); + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal void Dispose(bool disposing) + { + if (_disposed) + { + return; + } + + Disposing?.Invoke(this, EventArgs.Empty); + + _disposed = true; + + switch (_dcType) + { + case DeviceContextType.Information: + case DeviceContextType.NamedDevice: + Interop.Gdi32.DeleteDC(new HandleRef(this, _hDC)); + _hDC = IntPtr.Zero; + break; + case DeviceContextType.Memory: + Interop.Gdi32.DeleteDC(new HandleRef(this, _hDC)); + _hDC = IntPtr.Zero; + break; + // case DeviceContextType.Metafile: - not yet supported. + case DeviceContextType.Unknown: + default: + return; + // do nothing, the hdc is not owned by this object. + // in this case it is ok if disposed through finalization. + } + + DbgUtil.AssertFinalization(this, disposing); + } + + /// + /// Restores the device context to the specified state. The DC is restored by popping state information off a + /// stack created by earlier calls to the SaveHdc function. + /// The stack can contain the state information for several instances of the DC. If the state specified by the + /// specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top + /// of the stack and the specified instance. + /// Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific + /// instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative + /// to the current state. For example, -1 restores the most recently saved state. + /// See MSDN for more info. + /// + public void RestoreHdc() + { +#if TRACK_HDC + bool result = +#endif + // Note: Don't use the Hdc property here, it would force handle creation. + Interop.Gdi32.RestoreDC(new HandleRef(this, _hDC), -1); +#if TRACK_HDC + // Note: Winforms may call this method during app exit at which point the DC may have been finalized already causing this assert to popup. + Debug.WriteLine( DbgUtil.StackTraceToStr( string.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8}, state={2})", result, unchecked((int) _hDC), restoreState) )); +#endif + Debug.Assert(_contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()"); + + if (_contextStack != null) + { + GraphicsState g = (GraphicsState)_contextStack.Pop()!; + + _hCurrentBmp = g.hBitmap; + _hCurrentBrush = g.hBrush; + _hCurrentPen = g.hPen; + _hCurrentFont = g.hFont; + } + +#if OPTIMIZED_MEASUREMENTDC + // in this case, GDI will copy back the previously saved font into the DC. + // we dont actually know what the font is in our measurement DC so + // we need to clear it off. + MeasurementDCInfo.ResetIfIsMeasurementDC(_hDC); +#endif + } + + /// + /// Saves the current state of the device context by copying data describing selected objects and graphic + /// modes (such as the bitmap, brush, palette, font, pen, region, drawing mode, and mapping mode) to a + /// context stack. + /// The SaveDC function can be used any number of times to save any number of instances of the DC state. + /// A saved state can be restored by using the RestoreHdc method. + /// See MSDN for more details. + /// + public int SaveHdc() + { + HandleRef hdc = new HandleRef(this, _hDC); + int state = Interop.Gdi32.SaveDC(hdc); + + _contextStack ??= new Stack(); + + GraphicsState g = new GraphicsState(); + g.hBitmap = _hCurrentBmp; + g.hBrush = _hCurrentBrush; + g.hPen = _hCurrentPen; + g.hFont = _hCurrentFont; + + _contextStack.Push(g); + +#if TRACK_HDC + Debug.WriteLine( DbgUtil.StackTraceToStr( string.Format("state[0]=DC.SaveHdc(hDc=0x{1:x8})", state, unchecked((int) _hDC)) )); +#endif + + return state; + } + + /// + /// Selects a region as the current clipping region for the device context. + /// Remarks (From MSDN): + /// - Only a copy of the selected region is used. The region itself can be selected for any number of other device contexts or it can be deleted. + /// - The SelectClipRgn function assumes that the coordinates for a region are specified in device units. + /// - To remove a device-context's clipping region, specify a NULL region handle. + /// + public void SetClip(WindowsRegion region) + { + HandleRef hdc = new HandleRef(this, _hDC); + HandleRef hRegion = new HandleRef(region, region.HRegion); + + Interop.Gdi32.SelectClipRgn(hdc, hRegion); + } + + /// + /// Creates a new clipping region from the intersection of the current clipping region and the specified rectangle. + /// + public void IntersectClip(WindowsRegion wr) + { + //if the incoming windowsregion is infinite, there is no need to do any intersecting. + if (wr.HRegion == IntPtr.Zero) + { + return; + } + + WindowsRegion clip = new WindowsRegion(0, 0, 0, 0); + try + { + int result = Interop.Gdi32.GetClipRgn(new HandleRef(this, _hDC), new HandleRef(clip, clip.HRegion)); + + // If the function succeeds and there is a clipping region for the given device context, the return value is 1. + if (result == 1) + { + Debug.Assert(clip.HRegion != IntPtr.Zero); + wr.CombineRegion(clip, wr, Interop.Gdi32.CombineMode.RGN_AND); + } + + SetClip(wr); + } + finally + { + clip.Dispose(); + } + } + + /// + /// Modifies the viewport origin for a device context using the specified horizontal and vertical offsets in + /// logical units. + /// + public void TranslateTransform(int dx, int dy) + { + Point origin = default; + Interop.Gdi32.OffsetViewportOrgEx(new HandleRef(this, _hDC), dx, dy, ref origin); + } + + /// + /// + public override bool Equals(object? obj) + { + DeviceContext? other = obj as DeviceContext; + + if (other == this) + { + return true; + } + + if (other == null) + { + return false; + } + + // Note: Use property instead of field so the HDC is initialized. Also, this avoid serialization issues (the obj could be a proxy that does not have access to private fields). + return other.Hdc == _hDC; + } + + /// + /// This allows collections to treat DeviceContext objects wrapping the same HDC as the same objects. + /// + public override int GetHashCode() => _hDC.GetHashCode(); + + internal sealed class GraphicsState + { + internal IntPtr hBrush; + internal IntPtr hFont; + internal IntPtr hPen; + internal IntPtr hBitmap; + } + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs b/src/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs new file mode 100644 index 00000000000..afd80b750b6 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Internal +{ + /// + /// Represent the device type the context refers to. + /// + internal enum DeviceContextType + { + // Unknown device + Unknown = 0x00, + + // Window DC including non-client area - obtained from GetWindowDC + NCWindow = 0x02, + + // Printer DC - obtained from CreateDC. + NamedDevice = 0x03, + + // Information context - obtained from CreateIC. + Information = 0x04, + + // Memory dc - obtained from CreateCompatibleDC. + Memory = 0x05, + + // Metafile dc - obtained from CreateEnhMetafile. + Metafile = 0x06 // currently not supported. + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/DeviceContexts.cs b/src/System.Drawing.Common/src/misc/GDI/DeviceContexts.cs new file mode 100644 index 00000000000..a9169aef006 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/DeviceContexts.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Drawing.Internal +{ + /// + /// Keeps a cache of some graphics primitives. Created to improve performance of TextRenderer.MeasureText methods + /// that don't receive a WindowsGraphics. This class maintains a cache of MRU WindowsFont objects in the process. + /// + internal static class DeviceContexts + { + [ThreadStatic] + private static ClientUtils.WeakRefCollection? t_activeDeviceContexts; + + /// + /// WindowsGraphicsCacheManager needs to track DeviceContext objects so it can ask them if a font is in use + /// before they it's deleted. + /// + internal static void AddDeviceContext(DeviceContext dc) + { + ClientUtils.WeakRefCollection wrc = t_activeDeviceContexts ??= new ClientUtils.WeakRefCollection() + { + RefCheckThreshold = 20 + }; + + if (!wrc.Contains(dc)) + { + dc.Disposing += new EventHandler(OnDcDisposing); + wrc.Add(dc); + } + } + + private static void OnDcDisposing(object? sender, EventArgs e) + { + if (sender is DeviceContext dc) + { + dc.Disposing -= new EventHandler(OnDcDisposing); + RemoveDeviceContext(dc); + } + } + + internal static void RemoveDeviceContext(DeviceContext dc) + { + t_activeDeviceContexts?.RemoveByHashCode(dc); + } + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/WindowsGraphics.cs b/src/System.Drawing.Common/src/misc/GDI/WindowsGraphics.cs new file mode 100644 index 00000000000..37595989ad2 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/WindowsGraphics.cs @@ -0,0 +1,177 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// THIS PARTIAL CLASS CONTAINS THE BASE METHODS FOR CREATING AND DISPOSING A WINDOWSGRAPHICS AS WELL +// GETTING, DISPOSING AND WORKING WITH A DC. + +using System.Diagnostics; +using System.Drawing.Drawing2D; + +namespace System.Drawing.Internal +{ + /// + /// WindowsGraphics is a library for rendering text and drawing using GDI; it was created to address performance + /// and compatibility issues found in GDI+ Graphics class. + /// + /// Note: WindowsGraphics is a stateful component, DC properties are persisted from method calls, as opposed to + /// Graphics (GDI+) which performs atomic operations and always restores the hdc. The underlying hdc is always + /// saved and restored on dispose so external HDCs won't be modified by WindowsGraphics. So we don't need to + /// restore previous objects into the dc in method calls. + /// + internal sealed partial class WindowsGraphics : MarshalByRefObject, IDisposable, IDeviceContext + { + // Wrapper around the window dc this object refers to. + // Note: this dc is only disposed when owned (created) by the WindowsGraphics. + private DeviceContext _dc; + private bool _disposeDc; + private Graphics? _graphics; // cached when initialized FromGraphics to be able to call g.ReleaseHdc from Dispose. + +#if GDI_FINALIZATION_WATCH + private string AllocationSite = DbgUtil.StackTrace; +#endif + + public WindowsGraphics(DeviceContext dc) + { + Debug.Assert(dc != null, "null dc!"); + _dc = dc; + _dc.SaveHdc(); + } + + public static WindowsGraphics FromHdc(IntPtr hDc) + { + Debug.Assert(hDc != IntPtr.Zero, "null hDc"); + DeviceContext dc = DeviceContext.FromHdc(hDc); + + // we create it, we dispose it. + return new WindowsGraphics(dc) + { + _disposeDc = true + }; + } + + public static WindowsGraphics FromGraphics(Graphics g, ApplyGraphicsProperties properties) + { + Debug.Assert(g != null, "null Graphics object."); + + WindowsRegion? wr = null; + + PointF offset = default; + + if (properties != ApplyGraphicsProperties.None) + { + Region? clip = null; + +#if NETCOREAPP + if (properties.HasFlag(ApplyGraphicsProperties.Clipping)) + { + g.GetContextInfo(out offset, out clip); + } + else + { + g.GetContextInfo(out offset); + } +#else + Matrix? worldTransf = null; + if (g.GetContextInfo() is object[] data && data.Length == 2) + { + if (properties.HasFlag(ApplyGraphicsProperties.Clipping)) + { + clip = data[0] as Region; + } + worldTransf = data[1] as Matrix; + if (worldTransf != null) + { + offset = worldTransf.Offset; + } + } +#endif + + if (clip is not null) + { + // We have to create the WindowsRegion and dispose the Region object before locking the Graphics object, + // in case of an unlikely exception before releasing the WindowsRegion, the finalizer will do it for us. + // (no try-finally block since this method is used frequently - perf). + + // If clipping has not been set (Region.IsInfinite) GetContextInfo will return a null Region. + + wr = WindowsRegion.FromRegion(clip, g); // WindowsRegion will take ownership of the hRegion. + clip.Dispose(); // Disposing the Region object doesn't destroy the hRegion. + } + } + + WindowsGraphics wg = FromHdc(g.GetHdc()); // This locks the Graphics object. + wg._graphics = g; + + // Apply transform and clip + if (wr is not null) + { + using (wr) + { + // If the Graphics object was created from a native DC the actual clipping region is the intersection + // beteween the original DC clip region and the GDI+ one - for display Graphics it is the same as + // Graphics.VisibleClipBounds. + wg.DeviceContext.IntersectClip(wr); + } + } + + if (offset != default) + { + // elements (XFORM) = [eM11, eM12, eM21, eM22, eDx, eDy], eDx/eDy specify the translation offset. + wg.DeviceContext.TranslateTransform((int)offset.X, (int)offset.Y); + } + + return wg; + } + + ~WindowsGraphics() => Dispose(false); + + public DeviceContext DeviceContext => _dc; + + // Okay to suppress. + // "WindowsGraphics object does not own the Graphics object. For instance in a control's Paint event we pass + // the GraphicsContainer object to TextRenderer, which uses WindowsGraphics; if the Graphics object is disposed + // then further painting will be broken." + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + internal void Dispose(bool disposing) + { + if (_dc != null) + { + DbgUtil.AssertFinalization(this, disposing); + + try + { + // Restore original dc. + _dc.RestoreHdc(); + + if (_disposeDc) + { + _dc.Dispose(disposing); + } + + if (_graphics != null) // if created from a Graphics object... + { + _graphics.ReleaseHdcInternal(_dc.Hdc); + _graphics = null; + } + } + catch (Exception ex) when (!ClientUtils.IsSecurityOrCriticalException(ex)) + { + Debug.Fail("Exception thrown during disposing: \r\n" + ex.ToString()); + } + finally + { + _dc = null!; + } + } + } + + public IntPtr GetHdc() => _dc.Hdc; + + public void ReleaseHdc() => _dc.Dispose(); + } +} diff --git a/src/System.Drawing.Common/src/misc/GDI/WindowsRegion.cs b/src/System.Drawing.Common/src/misc/GDI/WindowsRegion.cs new file mode 100644 index 00000000000..5aa3ae6ceb9 --- /dev/null +++ b/src/System.Drawing.Common/src/misc/GDI/WindowsRegion.cs @@ -0,0 +1,150 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace System.Drawing.Internal +{ + /// + /// Encapsulates a GDI Region object. + /// + internal sealed partial class WindowsRegion : MarshalByRefObject, ICloneable, IDisposable + { + private IntPtr _nativeHandle; // The hRegion, this class always takes ownership of the hRegion. + private bool _ownHandle; + +#if GDI_FINALIZATION_WATCH + private string AllocationSite = DbgUtil.StackTrace; +#endif + + private WindowsRegion() + { + } + + public WindowsRegion(Rectangle rect) + { + CreateRegion(rect); + } + + public WindowsRegion(int x, int y, int width, int height) + { + CreateRegion(new Rectangle(x, y, width, height)); + } + + /// + /// Creates a WindowsRegion from a region handle, if 'takeOwnership' is true, the handle is added to the + /// HandleCollector and is removed & destroyed on dispose. + /// + public static WindowsRegion FromHregion(IntPtr hRegion, bool takeOwnership) + { + WindowsRegion wr = new WindowsRegion(); + + // Note: Passing IntPtr.Zero for hRegion is ok. GDI+ infinite regions will have hRegion == null. + // GDI's SelectClipRgn interprets null region handle as resetting the clip region (all region will be available for painting). + if (hRegion != IntPtr.Zero) + { + wr._nativeHandle = hRegion; + + if (takeOwnership) + { + wr._ownHandle = true; + } + } + return wr; + } + + /// + /// Creates a WindowsRegion from a System.Drawing.Region. + /// + public static WindowsRegion FromRegion(Region region, Graphics g) + { + if (region.IsInfinite(g)) + { + // An infinite region would cover the entire device region which is the same as + // not having a clipping region. Observe that this is not the same as having an + // empty region, which when clipping to it has the effect of excluding the entire + // device region. + // To remove the clip region from a dc the SelectClipRgn() function needs to be + // called with a null region ptr - that's why we use the empty constructor here. + // GDI+ will return IntPtr.Zero for Region.GetHrgn(Graphics) when the region is + // Infinite. + return new WindowsRegion(); + } + + return FromHregion(region.GetHrgn(g), true); + } + + public object Clone() + { + // WARNING: WindowsRegion currently supports rectangulare regions only, if the WindowsRegion was created + // from an HRegion and it is not rectangular this method won't work as expected. + // Note: This method is currently not used and is here just to implement ICloneable. + return IsInfinite ? + new WindowsRegion() : + new WindowsRegion(ToRectangle()); + } + + /// + /// Combines region1 & region2 into this region. The regions cannot be null. The three regions need not be + /// distinct. For example, the sourceRgn1 can equal this region. + /// + public Interop.RegionType CombineRegion(WindowsRegion region1, WindowsRegion region2, Interop.Gdi32.CombineMode mode) + { + return Interop.Gdi32.CombineRgn(new HandleRef(this, HRegion), new HandleRef(region1, region1.HRegion), new HandleRef(region2, region2.HRegion), mode); + } + + private void CreateRegion(Rectangle rect) + { + Debug.Assert(_nativeHandle == IntPtr.Zero, "nativeHandle should be null, we're leaking handle"); + _nativeHandle = Interop.Gdi32.CreateRectRgn(rect.X, rect.Y, rect.X + rect.Width, rect.Y + rect.Height); + _ownHandle = true; + } + + public void Dispose() => Dispose(true); + + public void Dispose(bool disposing) + { + if (_nativeHandle != IntPtr.Zero) + { + DbgUtil.AssertFinalization(this, disposing); + + if (_ownHandle) + { + Interop.Gdi32.DeleteObject(new HandleRef(this, _nativeHandle)); + } + + _nativeHandle = IntPtr.Zero; + + if (disposing) + { + GC.SuppressFinalize(this); + } + } + } + + ~WindowsRegion() => Dispose(false); + + /// + /// The native region handle. + /// + public IntPtr HRegion => _nativeHandle; + + public bool IsInfinite => _nativeHandle == IntPtr.Zero; + + /// + /// A rectangle representing the window region set with the SetWindowRgn function. + /// + public Rectangle ToRectangle() + { + if (IsInfinite) + { + return new Rectangle(-int.MaxValue, -int.MaxValue, int.MaxValue, int.MaxValue); + } + + Interop.Gdi32.RECT rect = default; + Interop.Gdi32.GetRgnBox(new HandleRef(this, _nativeHandle), ref rect); + return new Rectangle(new Point(rect.left, rect.top), rect.Size); + } + } +} diff --git a/src/System.Drawing.Common/src/packaging.targets b/src/System.Drawing.Common/src/packaging.targets new file mode 100644 index 00000000000..da9b38becbb --- /dev/null +++ b/src/System.Drawing.Common/src/packaging.targets @@ -0,0 +1,115 @@ + + + + true + + true + $(BeforePack);AddNETStandardCompatErrorFileForPackaging + false + $(RepoRoot)LICENSE.TXT + $(RepoRoot)THIRD-PARTY-NOTICES.TXT + $(MSBuildThisFileDirectory)useSharedDesignerContext.txt + $(MSBuildThisFileDirectory)_._ + + + + + + + + + + + + + + $(NoWarn);NU5128 + + + + + + + + + + + + + + + + + <_NETStandardCompatErrorFilePath>$(BaseIntermediateOutputPath)netstandardcompaterror_%(NETStandardCompatError.Identity).targets + <_NETStandardCompatErrorFileTarget>NETStandardCompatError_$(PackageId.Replace('.', '_'))_$([System.String]::new('%(NETStandardCompatError.Supported)').Replace('.', '_')) + <_NETStandardCompatErrorFileContent> + + + + +]]> + + <_NETStandardCompatErrorPlaceholderFilePackagePath>buildTransitive$([System.IO.Path]::DirectorySeparatorChar)%(NETStandardCompatError.Supported) + + + + + + <_PackageBuildFile Include="@(None->Metadata('PackagePath')); + @(Content->Metadata('PackagePath'))" /> + <_PackageBuildFile PackagePathWithoutFilename="$([System.IO.Path]::GetDirectoryName('%(Identity)'))" /> + + + + + + + + + + + + + + + diff --git a/src/System.Drawing.Common/src/resources.targets b/src/System.Drawing.Common/src/resources.targets new file mode 100644 index 00000000000..e37e9715255 --- /dev/null +++ b/src/System.Drawing.Common/src/resources.targets @@ -0,0 +1,29 @@ + + + $(MSBuildProjectDirectory)\Resources\Strings.resx + System + SR + FxResources.$(AssemblyName).$(StringResourcesClassName) + + + true + + true + + + + + + + + + + + diff --git a/src/System.Drawing.Common/src/useSharedDesignerContext.txt b/src/System.Drawing.Common/src/useSharedDesignerContext.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/System.Drawing.Common/tests/AssemblyInfo.cs b/src/System.Drawing.Common/tests/AssemblyInfo.cs new file mode 100644 index 00000000000..b1abf9bdf23 --- /dev/null +++ b/src/System.Drawing.Common/tests/AssemblyInfo.cs @@ -0,0 +1,8 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Xunit; + +[assembly: ActiveIssue("https://github.com/dotnet/runtime/issues/35917", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] +[assembly: SkipOnPlatform(TestPlatforms.Browser, "System.Drawing.Common is not supported on Browser")] diff --git a/src/System.Drawing.Common/tests/BitmapTests.cs b/src/System.Drawing.Common/tests/BitmapTests.cs new file mode 100644 index 00000000000..8bbb1077d4b --- /dev/null +++ b/src/System.Drawing.Common/tests/BitmapTests.cs @@ -0,0 +1,1757 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// (C) 2004 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2004,2006-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace System.Drawing.Tests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class BitmapTests : FileCleanupTestBase + { + public static IEnumerable Ctor_FilePath_TestData() + { + yield return new object[] { "16x16_one_entry_4bit.ico", 16, 16, PixelFormat.Format32bppArgb, ImageFormat.Icon }; + yield return new object[] { "bitmap_173x183_indexed_8bit.bmp", 173, 183, PixelFormat.Format8bppIndexed, ImageFormat.Bmp }; + yield return new object[] { "16x16_nonindexed_24bit.png", 16, 16, PixelFormat.Format24bppRgb, ImageFormat.Png }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_FilePath_TestData))] + public void Ctor_FilePath(string filename, int width, int height, PixelFormat pixelFormat, ImageFormat rawFormat) + { + using (var bitmap = new Bitmap(Helpers.GetTestBitmapPath(filename))) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(rawFormat, bitmap.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_FilePath_TestData))] + public void Ctor_FilePath_UseIcm(string filename, int width, int height, PixelFormat pixelFormat, ImageFormat rawFormat) + { + foreach (bool useIcm in new bool[] { true, false }) + { + using (var bitmap = new Bitmap(Helpers.GetTestBitmapPath(filename), useIcm)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(rawFormat, bitmap.RawFormat); + } + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Ctor_NullFilePath_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => new Bitmap((string)null)); + AssertExtensions.Throws("path", () => new Bitmap((string)null, false)); + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData("", "path")] + [InlineData("\0", "path")] + [InlineData("NoSuchPath", null)] + public void Ctor_InvalidFilePath_ThrowsArgumentException(string filename, string paramName) + { + AssertExtensions.Throws(paramName, null, () => new Bitmap(filename)); + AssertExtensions.Throws(paramName, null, () => new Bitmap(filename, false)); + AssertExtensions.Throws(paramName, null, () => new Bitmap(filename, true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Type_ResourceName() + { + using (var bitmap = new Bitmap(typeof(BitmapTests), "bitmap_173x183_indexed_8bit.bmp")) + { + Assert.Equal(173, bitmap.Width); + Assert.Equal(183, bitmap.Height); + Assert.Equal(PixelFormat.Format8bppIndexed, bitmap.PixelFormat); + Assert.Equal(ImageFormat.Bmp, bitmap.RawFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullType_ThrowsArgumentNullException() + { + AssertExtensions.Throws("type", () => new Bitmap(null, "name")); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(typeof(Bitmap), "")] + [InlineData(typeof(Bitmap), "bitmap_173x183_indexed_8bit.bmp")] + [InlineData(typeof(BitmapTests), "bitmap_173x183_INDEXED_8bit.bmp")] + [InlineData(typeof(BitmapTests), "empty.file")] + public void Ctor_InvalidResource_ThrowsArgumentException(Type type, string resource) + { + AssertExtensions.Throws(null, () => new Bitmap(type, resource)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_InvalidResource_ThrowsArgumentNullException() + { + AssertExtensions.Throws("resource", null, () => new Bitmap(typeof(Bitmap), null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_FilePath_TestData))] + public void Ctor_Stream(string filename, int width, int height, PixelFormat pixelFormat, ImageFormat rawFormat) + { + using (Stream stream = File.OpenRead(Helpers.GetTestBitmapPath(filename))) + using (var bitmap = new Bitmap(stream)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(rawFormat, bitmap.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_FilePath_TestData))] + public void Ctor_Stream_UseIcm(string filename, int width, int height, PixelFormat pixelFormat, ImageFormat rawFormat) + { + foreach (bool useIcm in new bool[] { true, false }) + { + using (Stream stream = File.OpenRead(Helpers.GetTestBitmapPath(filename))) + using (var bitmap = new Bitmap(stream, useIcm)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(rawFormat, bitmap.RawFormat); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullStream_ThrowsArgumentNullException() + { + AssertExtensions.Throws("stream", null, () => new Bitmap((Stream)null)); + AssertExtensions.Throws("stream", null, () => new Bitmap((Stream)null, false)); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Ctor_InvalidBytesInStream_ThrowsArgumentException() + { + using (var stream = new MemoryStream(new byte[0])) + { + AssertExtensions.Throws(null, () => new Bitmap(stream)); + AssertExtensions.Throws(null, () => new Bitmap(stream, false)); + AssertExtensions.Throws(null, () => new Bitmap(stream, true)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(10, 10)] + [InlineData(5, 15)] + public void Ctor_Width_Height(int width, int height) + { + using (var bitmap = new Bitmap(width, height)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(10, 10, PixelFormat.Format1bppIndexed)] + [InlineData(10, 10, PixelFormat.Format8bppIndexed)] + [InlineData(1, 1, PixelFormat.Format16bppArgb1555)] + [InlineData(1, 1, PixelFormat.Format16bppRgb555)] + [InlineData(1, 1, PixelFormat.Format16bppRgb565)] + [InlineData(1, 1, PixelFormat.Format16bppGrayScale)] + [InlineData(1, 1, PixelFormat.Format24bppRgb)] + [InlineData(1, 1, PixelFormat.Format32bppRgb)] + [InlineData(5, 15, PixelFormat.Format32bppArgb)] + [InlineData(1, 1, PixelFormat.Format32bppPArgb)] + [InlineData(10, 10, PixelFormat.Format48bppRgb)] + [InlineData(10, 10, PixelFormat.Format4bppIndexed)] + [InlineData(1, 1, PixelFormat.Format64bppArgb)] + [InlineData(1, 1, PixelFormat.Format64bppPArgb)] + public void Ctor_Width_Height_PixelFormat(int width, int height, PixelFormat pixelFormat) + { + using (var bitmap = new Bitmap(width, height, pixelFormat)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + public static IEnumerable Ctor_Width_Height_Stride_PixelFormat_Scan0_TestData() + { + yield return new object[] { 10, 10, 0, PixelFormat.Format8bppIndexed, IntPtr.Zero }; + yield return new object[] { 5, 15, int.MaxValue, PixelFormat.Format32bppArgb, IntPtr.Zero }; + yield return new object[] { 5, 15, int.MinValue, PixelFormat.Format24bppRgb, IntPtr.Zero }; + yield return new object[] { 1, 1, 1, PixelFormat.Format1bppIndexed, IntPtr.Zero }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Width_Height_Stride_PixelFormat_Scan0_TestData))] + public void Ctor_Width_Height_Stride_PixelFormat_Scan0(int width, int height, int stride, PixelFormat pixelFormat, IntPtr scan0) + { + using (var bitmap = new Bitmap(width, height, stride, pixelFormat, scan0)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(pixelFormat, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(ushort.MaxValue * 513)] + [InlineData(int.MaxValue)] + public void Ctor_InvalidWidth_ThrowsArgumentException(int width) + { + AssertExtensions.Throws(null, () => new Bitmap(width, 1)); + AssertExtensions.Throws(null, () => new Bitmap(width, 1, Graphics.FromImage(new Bitmap(1, 1)))); + AssertExtensions.Throws(null, () => new Bitmap(new Bitmap(1, 1), width, 1)); + AssertExtensions.Throws(null, () => new Bitmap(new Bitmap(1, 1), new Size(width, 1))); + AssertExtensions.Throws(null, () => new Bitmap(width, 1, PixelFormat.Format16bppArgb1555)); + AssertExtensions.Throws(null, () => new Bitmap(width, 1, 0, PixelFormat.Format16bppArgb1555, IntPtr.Zero)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(ushort.MaxValue * 513)] + [InlineData(int.MaxValue)] + public void Ctor_InvalidHeight_ThrowsArgumentException(int height) + { + AssertExtensions.Throws(null, () => new Bitmap(1, height)); + AssertExtensions.Throws(null, () => new Bitmap(1, height, Graphics.FromImage(new Bitmap(1, 1)))); + AssertExtensions.Throws(null, () => new Bitmap(new Bitmap(1, 1), 1, height)); + AssertExtensions.Throws(null, () => new Bitmap(new Bitmap(1, 1), new Size(1, height))); + AssertExtensions.Throws(null, () => new Bitmap(1, height, PixelFormat.Format16bppArgb1555)); + AssertExtensions.Throws(null, () => new Bitmap(1, height, 0, PixelFormat.Format16bppArgb1555, IntPtr.Zero)); + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(PixelFormat.Undefined - 1)] + [InlineData(PixelFormat.Undefined)] + [InlineData(PixelFormat.Gdi - 1)] + [InlineData(PixelFormat.Max)] + [InlineData(PixelFormat.Indexed)] + [InlineData(PixelFormat.Gdi)] + [InlineData(PixelFormat.Alpha)] + [InlineData(PixelFormat.PAlpha)] + [InlineData(PixelFormat.Extended)] + [InlineData(PixelFormat.Canonical)] + public void Ctor_InvalidPixelFormat_ThrowsArgumentException(PixelFormat format) + { + AssertExtensions.Throws(null, () => new Bitmap(1, 1, format)); + AssertExtensions.Throws(null, () => new Bitmap(1, 1, 0, format, IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_InvalidScan0_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Bitmap(1, 1, 0, PixelFormat.Format16bppArgb1555, (IntPtr)10)); + } + + public static IEnumerable Image_TestData() + { + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format16bppRgb555), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format16bppRgb565), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format24bppRgb), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format32bppArgb), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format32bppPArgb), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format48bppRgb), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format64bppArgb), 1, 1 }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format64bppPArgb), 1, 1 }; + + yield return new object[] { new Bitmap(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico")), 16, 16 }; + yield return new object[] { new Bitmap(Helpers.GetTestBitmapPath("16x16_nonindexed_24bit.png")), 32, 48 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Image_TestData))] + public void Ctor_Width_Height_Graphics(Bitmap image, int width, int height) + { + using (Graphics graphics = Graphics.FromImage(image)) + using (var bitmap = new Bitmap(width, height, graphics)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(PixelFormat.Format32bppPArgb, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullGraphics_ThrowsArgumentNullException() + { + AssertExtensions.Throws("g", null, () => new Bitmap(1, 1, null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Image() + { + using (var image = new Bitmap(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + using (var bitmap = new Bitmap(image)) + { + Assert.Equal(16, bitmap.Width); + Assert.Equal(16, bitmap.Height); + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullImageWithoutSize_ThrowsNullReferenceException() + { + Assert.Throws(() => new Bitmap((Image)null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Image_TestData))] + public void Ctor_Image_Width_Height(Image image, int width, int height) + { + using (var bitmap = new Bitmap(image, width, height)) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Image_TestData))] + public void Ctor_Size(Image image, int width, int height) + { + using (var bitmap = new Bitmap(image, new Size(width, height))) + { + Assert.Equal(width, bitmap.Width); + Assert.Equal(height, bitmap.Height); + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullImageWithSize_ThrowsArgumentNullException() + { + AssertExtensions.Throws("original", "image", () => new Bitmap(null, new Size(1, 2))); + AssertExtensions.Throws("original", "image", () => new Bitmap(null, 1, 2)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedImage_ThrowsArgumentException() + { + var image = new Bitmap(1, 1); + image.Dispose(); + + AssertExtensions.Throws(null, () => new Bitmap(image)); + AssertExtensions.Throws(null, () => new Bitmap(image, 1, 1)); + AssertExtensions.Throws(null, () => new Bitmap(image, new Size(1, 1))); + } + + public static IEnumerable Clone_TestData() + { + yield return new object[] { new Bitmap(3, 3, PixelFormat.Format32bppArgb), new Rectangle(0, 0, 3, 3), PixelFormat.Format32bppArgb }; + yield return new object[] { new Bitmap(3, 3, PixelFormat.Format32bppArgb), new Rectangle(0, 0, 3, 3), PixelFormat.Format24bppRgb }; + yield return new object[] { new Bitmap(3, 3, PixelFormat.Format1bppIndexed), new Rectangle(1, 1, 1, 1), PixelFormat.Format64bppArgb }; + yield return new object[] { new Bitmap(3, 3, PixelFormat.Format64bppPArgb), new Rectangle(1, 1, 1, 1), PixelFormat.Format16bppRgb565 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Clone_TestData))] + public void Clone_Rectangle_ReturnsExpected(Bitmap bitmap, Rectangle rectangle, PixelFormat targetFormat) + { + try + { + using (Bitmap clone = bitmap.Clone(rectangle, targetFormat)) + { + Assert.NotSame(bitmap, clone); + + Assert.Equal(rectangle.Width, clone.Width); + Assert.Equal(rectangle.Height, clone.Height); + Assert.Equal(targetFormat, clone.PixelFormat); + Assert.Equal(bitmap.RawFormat, clone.RawFormat); + + for (int x = 0; x < rectangle.Width; x++) + { + for (int y = 0; y < rectangle.Height; y++) + { + Color expectedColor = bitmap.GetPixel(rectangle.X + x, rectangle.Y + y); + if (Image.IsAlphaPixelFormat(targetFormat)) + { + Assert.Equal(expectedColor, clone.GetPixel(x, y)); + } + else + { + Assert.Equal(Color.FromArgb(255, expectedColor.R, expectedColor.G, expectedColor.B), clone.GetPixel(x, y)); + } + } + } + } + } + finally + { + bitmap.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Clone_TestData))] + public void Clone_RectangleF_ReturnsExpected(Bitmap bitmap, Rectangle rectangle, PixelFormat format) + { + try + { + using (Bitmap clone = bitmap.Clone((RectangleF)rectangle, format)) + { + Assert.NotSame(bitmap, clone); + + Assert.Equal(rectangle.Width, clone.Width); + Assert.Equal(rectangle.Height, clone.Height); + Assert.Equal(format, clone.PixelFormat); + } + } + finally + { + bitmap.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 1)] + [InlineData(1, 0)] + public void Clone_ZeroWidthOrHeightRect_ThrowsArgumentException(int width, int height) + { + using (var bitmap = new Bitmap(3, 3)) + { + AssertExtensions.Throws(null, () => bitmap.Clone(new Rectangle(0, 0, width, height), bitmap.PixelFormat)); + AssertExtensions.Throws(null, () => bitmap.Clone(new RectangleF(0, 0, width, height), bitmap.PixelFormat)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0, 4, 1)] + [InlineData(0, 0, 1, 4)] + [InlineData(1, 0, 3, 1)] + [InlineData(0, 1, 1, 3)] + [InlineData(4, 1, 1, 1)] + [InlineData(1, 4, 1, 1)] + public void Clone_InvalidRect_ThrowsOutOfMemoryException(int x, int y, int width, int height) + { + using (var bitmap = new Bitmap(3, 3)) + { + Assert.Throws(() => bitmap.Clone(new Rectangle(x, y, width, height), bitmap.PixelFormat)); + Assert.Throws(() => bitmap.Clone(new RectangleF(x, y, width, height), bitmap.PixelFormat)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Max)] + [InlineData(PixelFormat.Indexed)] + [InlineData(PixelFormat.Gdi)] + [InlineData(PixelFormat.Alpha)] + [InlineData(PixelFormat.PAlpha)] + [InlineData(PixelFormat.Extended)] + [InlineData(PixelFormat.Format16bppGrayScale)] + [InlineData(PixelFormat.Canonical)] + public void Clone_InvalidPixelFormat_ThrowsOutOfMemoryException(PixelFormat format) + { + using (var bitmap = new Bitmap(1, 1)) + { + Assert.Throws(() => bitmap.Clone(new Rectangle(0, 0, 1, 1), format)); + Assert.Throws(() => bitmap.Clone(new RectangleF(0, 0, 1, 1), format)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_GrayscaleFormat_ThrowsOutOfMemoryException() + { + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format16bppGrayScale)) + { + Assert.Throws(() => bitmap.Clone(new Rectangle(0, 0, 1, 1), PixelFormat.Format32bppArgb)); + Assert.Throws(() => bitmap.Clone(new RectangleF(0, 0, 1, 1), PixelFormat.Format32bppArgb)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_ValidBitmap_Success() + { + using (var bitmap = new Bitmap(1, 1)) + using (Bitmap clone = Assert.IsType(bitmap.Clone())) + { + Assert.NotSame(bitmap, clone); + Assert.Equal(1, clone.Width); + Assert.Equal(1, clone.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.Clone()); + AssertExtensions.Throws(null, () => bitmap.Clone(new Rectangle(0, 0, 1, 1), PixelFormat.Format32bppArgb)); + AssertExtensions.Throws(null, () => bitmap.Clone(new RectangleF(0, 0, 1, 1), PixelFormat.Format32bppArgb)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetFrameCount_NewBitmap_ReturnsZero() + { + using (var bitmap = new Bitmap(1, 1)) + { + Assert.Equal(1, bitmap.GetFrameCount(FrameDimension.Page)); + Assert.Equal(1, bitmap.GetFrameCount(FrameDimension.Resolution)); + Assert.Equal(1, bitmap.GetFrameCount(FrameDimension.Time)); + Assert.Equal(1, bitmap.GetFrameCount(new FrameDimension(Guid.Empty))); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetFrameCount_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.GetFrameCount(FrameDimension.Page)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + public void SelectActiveFrame_InvalidFrameIndex_ThrowsArgumentException(int index) + { + using (var bitmap = new Bitmap(1, 1)) + { + Assert.Equal(0, bitmap.SelectActiveFrame(FrameDimension.Page, index)); + Assert.Equal(0, bitmap.SelectActiveFrame(FrameDimension.Resolution, index)); + Assert.Equal(0, bitmap.SelectActiveFrame(FrameDimension.Time, index)); + Assert.Equal(0, bitmap.SelectActiveFrame(new FrameDimension(Guid.Empty), index)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SelectActiveFrame_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.SelectActiveFrame(FrameDimension.Page, 0)); + } + + public static IEnumerable GetPixel_TestData() + { + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format1bppIndexed), 0, 0, Color.FromArgb(0, 0, 0) }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format4bppIndexed), 0, 0, Color.FromArgb(0, 0, 0) }; + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format8bppIndexed), 0, 0, Color.FromArgb(0, 0, 0) }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), 0, 0, Color.FromArgb(0, 0, 0) }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), 99, 99, Color.FromArgb(0, 0, 0) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(GetPixel_TestData))] + public void GetPixel_ValidPixelFormat_Success(Bitmap bitmap, int x, int y, Color color) + { + try + { + Assert.Equal(color, bitmap.GetPixel(x, y)); + } + finally + { + bitmap.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(-1)] + [InlineData(1)] + public void GetPixel_InvalidX_ThrowsArgumentOutOfRangeException(int x) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws("x", () => bitmap.GetPixel(x, 0)); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(-1)] + [InlineData(1)] + public void GetPixel_InvalidY_ThrowsArgumentOutOfRangeException(int y) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws("y", () => bitmap.GetPixel(0, y)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetPixel_GrayScalePixelFormat_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format16bppGrayScale)) + { + AssertExtensions.Throws(null, () => bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetPixel_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.GetPixel(0, 0)); + } + + public static IEnumerable GetHbitmap_TestData() + { + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format32bppRgb), 1, 1 }; + yield return new object[] { new Bitmap(32, 32, PixelFormat.Format32bppArgb), 32, 32 }; + yield return new object[] { new Bitmap(512, 512, PixelFormat.Format16bppRgb555), 512, 512 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(GetHbitmap_TestData))] + public void GetHbitmap_FromHbitmap_ReturnsExpected(Bitmap bitmap, int width, int height) + { + IntPtr handle = bitmap.GetHbitmap(); + try + { + Assert.NotEqual(IntPtr.Zero, handle); + + using (Bitmap result = Image.FromHbitmap(handle)) + { + Assert.Equal(width, result.Width); + Assert.Equal(height, result.Height); + Assert.Equal(PixelFormat.Format32bppRgb, result.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, result.RawFormat); + } + } + finally + { + bitmap.Dispose(); + } + + // Hbitmap survives original bitmap disposal. + using (Bitmap result = Image.FromHbitmap(handle)) + { + Assert.Equal(width, result.Width); + Assert.Equal(height, result.Height); + Assert.Equal(PixelFormat.Format32bppRgb, result.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, result.RawFormat); + } + + // Hbitmap can be used multiple times. + using (Bitmap result = Image.FromHbitmap(handle)) + { + Assert.Equal(width, result.Width); + Assert.Equal(height, result.Height); + Assert.Equal(PixelFormat.Format32bppRgb, result.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, result.RawFormat); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1)] + [InlineData(short.MaxValue, 1)] + [InlineData(1, short.MaxValue)] + public void GetHbitmap_Grayscale_ThrowsArgumentException(int width, int height) + { + using (var bitmap = new Bitmap(width, height, PixelFormat.Format16bppGrayScale)) + { + AssertExtensions.Throws(null, () => bitmap.GetHbitmap()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHbitmap_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.GetHbitmap()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHbitmap_InvalidHandle_ThrowsExternalException() + { + Assert.Throws(() => Image.FromHbitmap(IntPtr.Zero)); + Assert.Throws(() => Image.FromHbitmap((IntPtr)10)); + } + + public static IEnumerable FromHicon_Icon_TestData() + { + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico")), 16, 16 }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("32x32_one_entry_4bit.ico")), 32, 32 }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico")), 64, 64 }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico")), 96, 96 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FromHicon_Icon_TestData))] + public void FromHicon_IconHandle_ReturnsExpected(Icon icon, int width, int height) + { + IntPtr handle; + try + { + using (Bitmap bitmap = GetHicon_FromHicon_ReturnsExpected(icon.Handle, width, height)) + { + handle = bitmap.GetHicon(); + } + } + finally + { + icon.Dispose(); + } + + // Hicon survives bitmap and icon disposal. + GetHicon_FromHicon_ReturnsExpected(handle, width, height); + } + + public static IEnumerable FromHicon_TestData() + { + yield return new object[] { new Bitmap(1, 1, PixelFormat.Format32bppRgb).GetHicon(), 1, 1 }; + yield return new object[] { new Bitmap(32, 32, PixelFormat.Format32bppRgb).GetHicon(), 32, 32 }; + yield return new object[] { new Bitmap(512, 512, PixelFormat.Format16bppRgb555).GetHicon(), 512, 512 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FromHicon_TestData))] + public Bitmap GetHicon_FromHicon_ReturnsExpected(IntPtr handle, int width, int height) + { + Assert.NotEqual(IntPtr.Zero, handle); + + Bitmap result = Bitmap.FromHicon(handle); + Assert.Equal(width, result.Width); + Assert.Equal(height, result.Height); + Assert.Equal(PixelFormat.Format32bppArgb, result.PixelFormat); + Assert.Equal(ImageFormat.MemoryBmp, result.RawFormat); + Assert.Equal(335888, result.Flags); + Assert.Empty(result.Palette.Entries); + + return result; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHicon_Grayscale_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format16bppGrayScale)) + { + AssertExtensions.Throws(null, () => bitmap.GetHicon()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHicon_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.GetHicon()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "In .NET Framework we use GDI 1.0")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/69471")] + public void SaveWmfAsPngDoesntChangeImageBoundaries() + { + if (PlatformDetection.IsWindows7) + { + throw new SkipTestException("GDI+ 1.1 is not supported"); + } + + if (PlatformDetection.IsArmOrArm64Process) + { + // https://github.com/dotnet/runtime/issues/28859 + throw new SkipTestException("Arm precision"); + } + + string output = GetTestFilePath() + ".png"; + using Stream wmfStream = File.OpenRead(Helpers.GetTestBitmapPath("gdiwmfboundariesbug.wmf")); + using Image bitmapFromWmf = Bitmap.FromStream(wmfStream); + bitmapFromWmf.Save(output, ImageFormat.Png); + + using Stream expectedPngStream = File.OpenRead(Helpers.GetTestBitmapPath("gdiwmfboundariesbug-output.png")); + using Image expectedPngBitmap = Bitmap.FromStream(expectedPngStream); + using MemoryStream expectedMemoryStream = new MemoryStream(); + expectedPngBitmap.Save(expectedMemoryStream, ImageFormat.Png); + + using Stream outputPngStream = File.OpenRead(output); + using Image outputPngBitmap = Bitmap.FromStream(outputPngStream); + using MemoryStream outputMemoryStream = new MemoryStream(); + outputPngBitmap.Save(outputMemoryStream, ImageFormat.Png); + + Assert.Equal(expectedMemoryStream.ToArray(), outputMemoryStream.ToArray()); + } + + // This test causes an AV on Linux + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHicon_InvalidHandle_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Bitmap.FromHicon(IntPtr.Zero)); + AssertExtensions.Throws(null, () => Bitmap.FromHicon((IntPtr)10)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHicon_1bppIcon_ThrowsArgumentException() + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"))) + { + AssertExtensions.Throws(null, () => Bitmap.FromHicon(icon.Handle)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromResource_InvalidHandle_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Bitmap.FromResource(IntPtr.Zero, "Name")); + AssertExtensions.Throws(null, () => Bitmap.FromResource((IntPtr)10, "Name")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromResource_InvalidBitmapName_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Bitmap.FromResource(IntPtr.Zero, "Name")); + AssertExtensions.Throws(null, () => Bitmap.FromResource((IntPtr)10, "Name")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeTransparent_NoColorWithMatches_SetsMatchingPixelsToTransparent() + { + using (var bitmap = new Bitmap(10, 10)) + { + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + if (y % 2 == 0) + { + bitmap.SetPixel(x, y, Color.LightGray); + } + else + { + bitmap.SetPixel(x, y, Color.Red); + } + } + } + + bitmap.MakeTransparent(); + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + if (y % 2 == 0) + { + Assert.Equal(Color.FromArgb(255, 211, 211, 211), bitmap.GetPixel(x, y)); + } + else + { + Assert.Equal(Color.FromArgb(0, 0, 0, 0), bitmap.GetPixel(x, y)); + } + } + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeTransparent_CustomColorExists_SetsMatchingPixelsToTransparent() + { + using (var bitmap = new Bitmap(10, 10)) + { + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + if (y % 2 == 0) + { + bitmap.SetPixel(x, y, Color.Blue); + } + else + { + bitmap.SetPixel(x, y, Color.Red); + } + } + } + + bitmap.MakeTransparent(Color.Blue); + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + if (y % 2 == 0) + { + Assert.Equal(Color.FromArgb(0, 0, 0, 0), bitmap.GetPixel(x, y)); + } + else + { + Assert.Equal(Color.FromArgb(255, 255, 0, 0), bitmap.GetPixel(x, y)); + } + } + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeTransparent_CustomColorDoesntExist_DoesNothing() + { + using (var bitmap = new Bitmap(10, 10)) + { + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + bitmap.SetPixel(x, y, Color.Blue); + } + } + + bitmap.MakeTransparent(Color.Red); + for (int x = 0; x < bitmap.Width; x++) + { + for (int y = 0; y < bitmap.Height; y++) + { + Assert.Equal(Color.FromArgb(255, 0, 0, 255), bitmap.GetPixel(x, y)); + } + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeTransparent_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.MakeTransparent()); + AssertExtensions.Throws(null, () => bitmap.MakeTransparent(Color.Red)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22629", TargetFrameworkMonikers.NetFramework)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/26247", TestPlatforms.Windows)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeTransparent_GrayscalePixelFormat_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format16bppGrayScale)) + { + AssertExtensions.Throws(null, () => bitmap.MakeTransparent()); + Assert.Throws(() => bitmap.MakeTransparent(Color.Red)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void MakeTransparent_Icon_ThrowsInvalidOperationException() + { + using (var bitmap = new Bitmap(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + { + Assert.Throws(() => bitmap.MakeTransparent(Color.Red)); + } + } + + public static IEnumerable SetPixel_TestData() + { + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), 0, 0, Color.FromArgb(255, 128, 128, 128) }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), 99, 99, Color.FromArgb(255, 128, 128, 128) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetPixel_TestData))] + public void SetPixel_ValidPixelFormat_Success(Bitmap bitmap, int x, int y, Color color) + { + bitmap.SetPixel(x, y, color); + Assert.Equal(color, bitmap.GetPixel(x, y)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Format1bppIndexed)] + [InlineData(PixelFormat.Format4bppIndexed)] + [InlineData(PixelFormat.Format8bppIndexed)] + public void SetPixel_IndexedPixelFormat_ThrowsInvalidOperationException(PixelFormat format) + { + using (var bitmap = new Bitmap(1, 1, format)) + { + Assert.Throws(() => bitmap.SetPixel(0, 0, Color.Red)); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(-1)] + [InlineData(1)] + public void SetPixel_InvalidX_ThrowsArgumentOutOfRangeException(int x) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws("x", () => bitmap.SetPixel(x, 0, Color.Red)); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(-1)] + [InlineData(1)] + public void SetPixel_InvalidY_ThrowsArgumentOutOfRangeException(int y) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws("y", () => bitmap.SetPixel(0, y, Color.Red)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetPixel_GrayScalePixelFormat_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format16bppGrayScale)) + { + AssertExtensions.Throws(null, () => bitmap.SetPixel(0, 0, Color.Red)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetPixel_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.SetPixel(0, 0, Color.Red)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1)] + [InlineData(float.PositiveInfinity, float.PositiveInfinity)] + [InlineData(float.MaxValue, float.MaxValue)] + public void SetResolution_ValidDpi_Success(float xDpi, float yDpi) + { + using (var bitmap = new Bitmap(1, 1)) + { + bitmap.SetResolution(xDpi, yDpi); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(float.NaN)] + [InlineData(float.NegativeInfinity)] + public void SetResolution_InvalidXDpi_ThrowsArgumentException(float xDpi) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.SetResolution(xDpi, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(float.NaN)] + [InlineData(float.NegativeInfinity)] + public void SetResolution_InvalidYDpi_ThrowsArgumentException(float yDpi) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.SetResolution(1, yDpi)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.SetResolution(1, 1)); + } + + public static IEnumerable LockBits_TestData() + { + Bitmap bitmap() => new Bitmap(2, 2, PixelFormat.Format32bppArgb); + yield return new object[] { bitmap(), new Rectangle(0, 0, 2, 2), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb, 8, 1 }; + yield return new object[] { bitmap(), new Rectangle(0, 0, 2, 2), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb, 8, 3 }; + yield return new object[] { bitmap(), new Rectangle(0, 0, 2, 2), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb, 8, 2 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb, 400, 1 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb, 400, 3 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb, 400, 2 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb, 300, 65537 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb, 300, 65539 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb, 300, 65538 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb, 300, 1 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb, 300, 3 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb, 300, 2 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb, 400, 65537 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb, 400, 65539 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format24bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb, 400, 65538 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb, 300, 65537 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed, 100, 1 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed, 100, 3 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed, 100, 2 }; + + + yield return new object[] { new Bitmap(184, 184, PixelFormat.Format1bppIndexed), new Rectangle(0, 0, 184, 184), ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed, 24, 1 }; + yield return new object[] { new Bitmap(184, 184, PixelFormat.Format1bppIndexed), new Rectangle(0, 0, 184, 184), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed, 24, 3 }; + yield return new object[] { new Bitmap(184, 184, PixelFormat.Format1bppIndexed), new Rectangle(0, 0, 184, 184), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed, 24, 2 }; + + yield return new object[] { bitmap(), new Rectangle(1, 1, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb, 8, 1 }; + yield return new object[] { bitmap(), new Rectangle(1, 1, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb, 8, 3 }; + yield return new object[] { bitmap(), new Rectangle(1, 1, 1, 1), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb, 8, 2 }; + + yield return new object[] { bitmap(), new Rectangle(1, 1, 1, 1), ImageLockMode.ReadOnly - 1, PixelFormat.Format32bppArgb, 8, 0 }; + + yield return new object[] { bitmap(), new Rectangle(0, 0, 2, 2), ImageLockMode.WriteOnly, PixelFormat.Format16bppGrayScale, 4, 65538 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed, 100, 65537 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed, 100, 65539 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format32bppRgb), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed, 100, 65538 }; + + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb, 300, 65539 }; + yield return new object[] { new Bitmap(100, 100, PixelFormat.Format8bppIndexed), new Rectangle(0, 0, 100, 100), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb, 300, 65538 }; + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/30565", TestPlatforms.Windows)] + [MemberData(nameof(LockBits_TestData))] + public void LockBits_Invoke_Success(Bitmap bitmap, Rectangle rectangle, ImageLockMode lockMode, PixelFormat pixelFormat, int expectedStride, int expectedReserved) + { + try + { + BitmapData data = bitmap.LockBits(rectangle, lockMode, pixelFormat); + Assert.Equal(pixelFormat, data.PixelFormat); + Assert.Equal(rectangle.Width, data.Width); + Assert.Equal(rectangle.Height, data.Height); + Assert.Equal(expectedStride, data.Stride); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // "Reserved" is documented as "Reserved. Do not use.", so it's not clear whether we actually need to test this in any unit tests. + Assert.Equal(expectedReserved, data.Reserved); + } + + // Locking with 16bppGrayscale succeeds, but the data can't be unlocked. + if (pixelFormat == PixelFormat.Format16bppGrayScale) + { + AssertExtensions.Throws(null, () => bitmap.UnlockBits(data)); + } + else + { + bitmap.UnlockBits(data); + } + } + finally + { + bitmap.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LockBits_NullBitmapData_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.LockBits(Rectangle.Empty, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb, null)); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(-1, 0, 1, 1)] + [InlineData(2, 0, 1, 1)] + [InlineData(0, -1, 1, 1)] + [InlineData(0, 2, 1, 1)] + [InlineData(0, 0, -1, 1)] + [InlineData(0, 0, 3, 1)] + [InlineData(0, 0, 1, -1)] + [InlineData(0, 0, 1, 3)] + [InlineData(1, 0, 2, 1)] + [InlineData(1, 1, 1, 0)] + [InlineData(1, 1, 0, 1)] + public void LockBits_InvalidRect_ThrowsArgumentException(int x, int y, int width, int height) + { + using (var bitmap = new Bitmap(2, 2)) + { + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(x, y, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat)); + + var bitmapData = new BitmapData(); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(x, y, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat, bitmapData)); + Assert.Equal(IntPtr.Zero, bitmapData.Scan0); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(PixelFormat.DontCare)] + [InlineData(PixelFormat.Max)] + [InlineData(PixelFormat.Indexed)] + [InlineData(PixelFormat.Gdi)] + [InlineData(PixelFormat.Alpha)] + [InlineData(PixelFormat.PAlpha)] + [InlineData(PixelFormat.Extended)] + [InlineData(PixelFormat.Canonical)] + public void LockBits_InvalidPixelFormat_ThrowsArgumentException(PixelFormat format) + { + using (var bitmap = new Bitmap(1, 1)) + { + foreach (ImageLockMode lockMode in Enum.GetValues(typeof(ImageLockMode))) + { + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), lockMode, format)); + + var bitmapData = new BitmapData(); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), lockMode, format, bitmapData)); + Assert.Equal(IntPtr.Zero, bitmapData.Scan0); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LockBits_ReadOnlyGrayscale_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format16bppGrayScale)); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format16bppGrayScale, new BitmapData())); + + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format16bppGrayScale)); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format16bppGrayScale, new BitmapData())); + + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.WriteOnly, PixelFormat.Format16bppGrayScale); + AssertExtensions.Throws(null, () => bitmap.UnlockBits(data)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData((ImageLockMode)(-1))] + [InlineData(ImageLockMode.UserInputBuffer + 1)] + [InlineData(ImageLockMode.UserInputBuffer)] + public void LockBits_InvalidLockMode_ThrowsArgumentException(ImageLockMode lockMode) + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), lockMode, bitmap.PixelFormat)); + + var bitmapData = new BitmapData(); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), lockMode, bitmap.PixelFormat, bitmapData)); + Assert.Equal(IntPtr.Zero, bitmapData.Scan0); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LockBits_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb)); + + var bitmapData = new BitmapData(); + AssertExtensions.Throws(null, () => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb, bitmapData)); + Assert.Equal(IntPtr.Zero, bitmapData.Scan0); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LockBits_AlreadyLocked_ThrowsInvalidOperationException() + { + using (var bitmap = new Bitmap(1, 1)) + { + bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat); + + Assert.Throws(() => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat)); + Assert.Throws(() => bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat, new BitmapData())); + + Assert.Throws(() => bitmap.LockBits(new Rectangle(1, 1, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat)); + Assert.Throws(() => bitmap.LockBits(new Rectangle(1, 1, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat, new BitmapData())); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, -1)] + [InlineData(0, 2)] + [InlineData(1, 2)] + public void UnlockBits_InvalidHeightWidth_Nop(int offset, int invalidParameter) + { + using (var bitmap = new Bitmap(2, 2)) + { + BitmapData data = bitmap.LockBits(new Rectangle(offset, offset, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat); + data.Height = invalidParameter; + data.Width = invalidParameter; + + bitmap.UnlockBits(data); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UnlockBits_Scan0Zero_Nop() + { + using (var bitmap = new Bitmap(1, 1)) + { + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat); + data.Scan0 = IntPtr.Zero; + + bitmap.UnlockBits(data); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(PixelFormat.Indexed)] + [InlineData(PixelFormat.Gdi)] + public void UnlockBits_InvalidPixelFormat_Nop(PixelFormat format) + { + using (var bitmap = new Bitmap(1, 1)) + { + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat); + data.PixelFormat = format; + + bitmap.UnlockBits(data); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UnlockBits_NullBitmapData_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(1, 1)) + { + AssertExtensions.Throws(null, () => bitmap.UnlockBits(null)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void UnlockBits_NotLocked_ThrowsExternalException() + { + using (var bitmap = new Bitmap(1, 1)) + { + Assert.Throws(() => bitmap.UnlockBits(new BitmapData())); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void UnlockBits_AlreadyUnlocked_ThrowsExternalException() + { + using (var bitmap = new Bitmap(1, 1)) + { + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, bitmap.PixelFormat); + bitmap.UnlockBits(data); + + Assert.Throws(() => bitmap.UnlockBits(new BitmapData())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UnlockBits_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.UnlockBits(new BitmapData())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Size_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.Width); + AssertExtensions.Throws(null, () => bitmap.Height); + AssertExtensions.Throws(null, () => bitmap.Size); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Format16bppArgb1555)] + [InlineData(PixelFormat.Format16bppRgb555)] + [InlineData(PixelFormat.Format16bppRgb565)] + [InlineData(PixelFormat.Format32bppArgb)] + [InlineData(PixelFormat.Format32bppPArgb)] + [InlineData(PixelFormat.Format32bppRgb)] + [InlineData(PixelFormat.Format24bppRgb)] + public void CustomPixelFormat_GetPixels_ReturnsExpected(PixelFormat format) + { + bool alpha = Image.IsAlphaPixelFormat(format); + int size = Image.GetPixelFormatSize(format) / 8 * 2; + using (var bitmap = new Bitmap(2, 1, format)) + { + Color a = Color.FromArgb(128, 64, 32, 16); + Color b = Color.FromArgb(192, 96, 48, 24); + bitmap.SetPixel(0, 0, a); + bitmap.SetPixel(1, 0, b); + Color c = bitmap.GetPixel(0, 0); + Color d = bitmap.GetPixel(1, 0); + if (size == 4) + { + Assert.Equal(255, c.A); + Assert.Equal(66, c.R); + if (format == PixelFormat.Format16bppRgb565) + { + Assert.Equal(32, c.G); + } + else + { + Assert.Equal(33, c.G); + } + Assert.Equal(16, c.B); + + Assert.Equal(255, d.A); + Assert.Equal(99, d.R); + if (format == PixelFormat.Format16bppRgb565) + { + Assert.Equal(48, d.G); + } + else + { + Assert.Equal(49, d.G); + } + Assert.Equal(24, d.B); + } + else if (alpha) + { + if (format == PixelFormat.Format32bppPArgb) + { + Assert.Equal(a.A, c.A); + Assert.Equal(a.R - 1, c.R); + Assert.Equal(a.G - 1, c.G); + Assert.Equal(a.B - 1, c.B); + + Assert.Equal(b.A, d.A); + Assert.Equal(b.R - 1, d.R); + Assert.Equal(b.G - 1, d.G); + Assert.Equal(b.B - 1, d.B); + } + else + { + Assert.Equal(a, c); + Assert.Equal(b, d); + } + } + else + { + Assert.Equal(Color.FromArgb(255, 64, 32, 16), c); + Assert.Equal(Color.FromArgb(255, 96, 48, 24), d); + } + BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, 2, 1), ImageLockMode.ReadOnly, format); + try + { + byte[] data = new byte[size]; + Marshal.Copy(bitmapData.Scan0, data, 0, size); + if (format == PixelFormat.Format32bppPArgb) + { + Assert.Equal(Math.Ceiling((float)c.B * c.A / 255), data[0]); + Assert.Equal(Math.Ceiling((float)c.G * c.A / 255), data[1]); + Assert.Equal(Math.Ceiling((float)c.R * c.A / 255), data[2]); + Assert.Equal(c.A, data[3]); + Assert.Equal(Math.Ceiling((float)d.B * d.A / 255), data[4]); + Assert.Equal(Math.Ceiling((float)d.G * d.A / 255), data[5]); + Assert.Equal(Math.Ceiling((float)d.R * d.A / 255), data[6]); + Assert.Equal(d.A, data[7]); + } + else if (size == 4) + { + switch (format) + { + case PixelFormat.Format16bppRgb565: + Assert.Equal(2, data[0]); + Assert.Equal(65, data[1]); + Assert.Equal(131, data[2]); + Assert.Equal(97, data[3]); + break; + case PixelFormat.Format16bppArgb1555: + Assert.Equal(130, data[0]); + Assert.Equal(160, data[1]); + Assert.Equal(195, data[2]); + Assert.Equal(176, data[3]); + break; + case PixelFormat.Format16bppRgb555: + Assert.Equal(130, data[0]); + Assert.Equal(32, data[1]); + Assert.Equal(195, data[2]); + Assert.Equal(48, data[3]); + break; + } + } + else + { + int n = 0; + Assert.Equal(c.B, data[n++]); + Assert.Equal(c.G, data[n++]); + Assert.Equal(c.R, data[n++]); + if (size % 4 == 0) + { + if (format == PixelFormat.Format32bppRgb) + { + Assert.Equal(128, data[n++]); + } + else + { + Assert.Equal(c.A, data[n++]); + } + } + Assert.Equal(d.B, data[n++]); + Assert.Equal(d.G, data[n++]); + Assert.Equal(d.R, data[n++]); + if (size % 4 == 0) + { + if (format == PixelFormat.Format32bppRgb) + { + Assert.Equal(192, data[n++]); + } + else + { + Assert.Equal(d.A, data[n++]); + } + } + } + } + finally + { + bitmap.UnlockBits(bitmapData); + } + } + } + + public static IEnumerable Palette_TestData() + { + yield return new object[] { PixelFormat.Format1bppIndexed, new int[] { -16777216, -1 } }; + yield return new object[] { PixelFormat.Format4bppIndexed, new int[] { -16777216, -8388608, -16744448, -8355840, -16777088, -8388480, -16744320, -8355712, -4144960, -65536, -16711936, -256, -16776961, -65281, -16711681, -1, } }; + yield return new object[] { PixelFormat.Format8bppIndexed, new int[] { -16777216, -8388608, -16744448, -8355840, -16777088, -8388480, -16744320, -8355712, -4144960, -65536, -16711936, -256, -16776961, -65281, -16711681, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -16777216, -16777165, -16777114, -16777063, -16777012, -16776961, -16764160, -16764109, -16764058, -16764007, -16763956, -16763905, -16751104, -16751053, -16751002, -16750951, -16750900, -16750849, -16738048, -16737997, -16737946, -16737895, -16737844, -16737793, -16724992, -16724941, -16724890, -16724839, -16724788, -16724737, -16711936, -16711885, -16711834, -16711783, -16711732, -16711681, -13434880, -13434829, -13434778, -13434727, -13434676, -13434625, -13421824, -13421773, -13421722, -13421671, -13421620, -13421569, -13408768, -13408717, -13408666, -13408615, -13408564, -13408513, -13395712, -13395661, -13395610, -13395559, -13395508, -13395457, -13382656, -13382605, -13382554, -13382503, -13382452, -13382401, -13369600, -13369549, -13369498, -13369447, -13369396, -13369345, -10092544, -10092493, -10092442, -10092391, -10092340, -10092289, -10079488, -10079437, -10079386, -10079335, -10079284, -10079233, -10066432, -10066381, -10066330, -10066279, -10066228, -10066177, -10053376, -10053325, -10053274, -10053223, -10053172, -10053121, -10040320, -10040269, -10040218, -10040167, -10040116, -10040065, -10027264, -10027213, -10027162, -10027111, -10027060, -10027009, -6750208, -6750157, -6750106, -6750055, -6750004, -6749953, -6737152, -6737101, -6737050, -6736999, -6736948, -6736897, -6724096, -6724045, -6723994, -6723943, -6723892, -6723841, -6711040, -6710989, -6710938, -6710887, -6710836, -6710785, -6697984, -6697933, -6697882, -6697831, -6697780, -6697729, -6684928, -6684877, -6684826, -6684775, -6684724, -6684673, -3407872, -3407821, -3407770, -3407719, -3407668, -3407617, -3394816, -3394765, -3394714, -3394663, -3394612, -3394561, -3381760, -3381709, -3381658, -3381607, -3381556, -3381505, -3368704, -3368653, -3368602, -3368551, -3368500, -3368449, -3355648, -3355597, -3355546, -3355495, -3355444, -3355393, -3342592, -3342541, -3342490, -3342439, -3342388, -3342337, -65536, -65485, -65434, -65383, -65332, -65281, -52480, -52429, -52378, -52327, -52276, -52225, -39424, -39373, -39322, -39271, -39220, -39169, -26368, -26317, -26266, -26215, -26164, -26113, -13312, -13261, -13210, -13159, -13108, -13057, -256, -205, -154, -103, -52, -1 } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Palette_TestData))] + public void Palette_Get_ReturnsExpected(PixelFormat pixelFormat, int[] expectedEntries) + { + using (var bitmap = new Bitmap(1, 1, pixelFormat)) + { + Assert.Equal(expectedEntries, bitmap.Palette.Entries.Select(c => c.ToArgb())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Palette_SetNull_ThrowsNullReferenceException() + { + using (var bitmap = new Bitmap(1, 1)) + { + Assert.Throws(() => bitmap.Palette = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Palette_Disposed_ThrowsArgumentException() + { + var bitmap = new Bitmap(1, 1); + ColorPalette palette = bitmap.Palette; + bitmap.Dispose(); + + AssertExtensions.Throws(null, () => bitmap.Palette); + AssertExtensions.Throws(null, () => bitmap.Palette = palette); + AssertExtensions.Throws(null, () => bitmap.Size); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBits_Marshalling_Success() + { + Color red = Color.FromArgb(Color.Red.ToArgb()); + Color blue = Color.FromArgb(Color.Blue.ToArgb()); + + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format32bppRgb)) + { + bitmap.SetPixel(0, 0, red); + Color pixelColor = bitmap.GetPixel(0, 0); + Assert.Equal(red, pixelColor); + + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + try + { + int pixelValue = Marshal.ReadByte(data.Scan0, 0); + pixelValue |= Marshal.ReadByte(data.Scan0, 1) << 8; + pixelValue |= Marshal.ReadByte(data.Scan0, 2) << 16; + pixelValue |= Marshal.ReadByte(data.Scan0, 3) << 24; + + pixelColor = Color.FromArgb(pixelValue); + // Disregard alpha information in the test + pixelColor = Color.FromArgb(red.A, pixelColor.R, pixelColor.G, pixelColor.B); + Assert.Equal(red, pixelColor); + + // write blue but we're locked in read-only... + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + Marshal.WriteByte(data.Scan0, 3, blue.A); + } + finally + { + bitmap.UnlockBits(data); + pixelColor = bitmap.GetPixel(0, 0); + // Disregard alpha information in the test + pixelColor = Color.FromArgb(red.A, pixelColor.R, pixelColor.G, pixelColor.B); + // ...so we still read red after unlocking + Assert.Equal(red, pixelColor); + } + + data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + try + { + // write blue + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + Marshal.WriteByte(data.Scan0, 3, blue.A); + } + finally + { + bitmap.UnlockBits(data); + pixelColor = bitmap.GetPixel(0, 0); + // Disregard alpha information in the test + pixelColor = Color.FromArgb(blue.A, pixelColor.R, pixelColor.G, pixelColor.B); + // read blue + Assert.Equal(blue, pixelColor); + } + } + + using (var bitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) + { + bitmap.SetPixel(0, 0, red); + + BitmapData data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + byte b = Marshal.ReadByte(data.Scan0, 0); + byte g = Marshal.ReadByte(data.Scan0, 1); + byte r = Marshal.ReadByte(data.Scan0, 2); + Assert.Equal(red, Color.FromArgb(red.A, r, g, b)); + // write blue but we're locked in read-only... + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + } + finally + { + bitmap.UnlockBits(data); + // ...so we still read red after unlocking + Assert.Equal(red, bitmap.GetPixel(0, 0)); + } + + data = bitmap.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + try + { + // write blue + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + } + finally + { + bitmap.UnlockBits(data); + // read blue + Assert.Equal(blue, bitmap.GetPixel(0, 0)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromNonSeekableStream() + { + string path = GetTestFilePath(); + using (Bitmap bitmap = new Bitmap(100, 100)) + { + bitmap.Save(path, ImageFormat.Png); + } + + using (FileStream stream = new FileStream(path, FileMode.Open)) + { + using (Bitmap bitmap = new Bitmap(new TestStream(stream, canSeek: false))) + { + Assert.Equal(100, bitmap.Height); + Assert.Equal(100, bitmap.Width); + Assert.Equal(ImageFormat.Png, bitmap.RawFormat); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(true, false)] + [InlineData(false, false)] + [InlineData(false, true)] + public void SaveToRestrictiveStream(bool canRead, bool canSeek) + { + using (Stream backingStream = new MemoryStream()) + using (Stream restrictiveStream = new TestStream(backingStream, canRead, canSeek)) + { + using (Bitmap bitmap = new Bitmap(100, 100)) + { + bitmap.Save(restrictiveStream, ImageFormat.Png); + } + + backingStream.Position = 0; + + using (Bitmap bitmap = new Bitmap(backingStream)) + { + Assert.Equal(100, bitmap.Height); + Assert.Equal(100, bitmap.Width); + Assert.Equal(ImageFormat.Png, bitmap.RawFormat); + } + } + } + + private class TestStream : Stream + { + private Stream _stream; + private bool _canRead; + private bool _canSeek; + + public TestStream(Stream stream, bool canRead = true, bool canSeek = true) + { + _stream = stream; + _canRead = canRead; + _canSeek = canSeek; + } + + public override bool CanRead => _canRead && _stream.CanRead; + public override bool CanSeek => _canSeek && _stream.CanSeek; + public override bool CanWrite => _stream.CanWrite; + public override long Length => _stream.Length; + public override long Position + { + get => _stream.Position; + set => _stream.Position = _canSeek ? value : throw new NotSupportedException(); + } + public override void Flush() => _stream.Flush(); + public override int Read(byte[] buffer, int offset, int count) => _canRead ? _stream.Read(buffer, offset, count) : throw new NotSupportedException(); + public override long Seek(long offset, SeekOrigin origin) => _stream.Seek(offset, origin); + public override void SetLength(long value) => _stream.SetLength(value); + public override void Write(byte[] buffer, int offset, int count) => _stream.Write(buffer, offset, count); + } + } +} diff --git a/src/System.Drawing.Common/tests/BrushTests.cs b/src/System.Drawing.Common/tests/BrushTests.cs new file mode 100644 index 00000000000..5cf621afa22 --- /dev/null +++ b/src/System.Drawing.Common/tests/BrushTests.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class BrushTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetNativeBrush_Brush_Success() + { + using (var brush = new SubBrush()) + { + brush.PublicSetNativeBrush((IntPtr)10); + brush.PublicSetNativeBrush(IntPtr.Zero); + + brush.PublicSetNativeBrush((IntPtr)10); + brush.PublicSetNativeBrush(IntPtr.Zero); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/30157")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_NoSuchEntryPoint_SilentyCatchesException() + { + var brush = new SubBrush(); + brush.PublicSetNativeBrush((IntPtr)10); + + // No EntryPointNotFoundException will be thrown. + brush.Dispose(); + } + + private class SubBrush : Brush + { + public override object Clone() => this; + public void PublicSetNativeBrush(IntPtr brush) => SetNativeBrush(brush); + } + } +} diff --git a/src/System.Drawing.Common/tests/BrushesTests.cs b/src/System.Drawing.Common/tests/BrushesTests.cs new file mode 100644 index 00000000000..92dc56453e4 --- /dev/null +++ b/src/System.Drawing.Common/tests/BrushesTests.cs @@ -0,0 +1,182 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Reflection; +using Xunit; + +namespace System.Drawing.Tests +{ + public class BrushesTests + { + public static IEnumerable Brushes_TestData() + { + yield return Brush(() => Brushes.AliceBlue, Color.AliceBlue); + yield return Brush(() => Brushes.AntiqueWhite, Color.AntiqueWhite); + yield return Brush(() => Brushes.Aqua, Color.Aqua); + yield return Brush(() => Brushes.Aquamarine, Color.Aquamarine); + yield return Brush(() => Brushes.Azure, Color.Azure); + yield return Brush(() => Brushes.Beige, Color.Beige); + yield return Brush(() => Brushes.Bisque, Color.Bisque); + yield return Brush(() => Brushes.Black, Color.Black); + yield return Brush(() => Brushes.BlanchedAlmond, Color.BlanchedAlmond); + yield return Brush(() => Brushes.Blue, Color.Blue); + yield return Brush(() => Brushes.BlueViolet, Color.BlueViolet); + yield return Brush(() => Brushes.Brown, Color.Brown); + yield return Brush(() => Brushes.BurlyWood, Color.BurlyWood); + yield return Brush(() => Brushes.CadetBlue, Color.CadetBlue); + yield return Brush(() => Brushes.Chartreuse, Color.Chartreuse); + yield return Brush(() => Brushes.Chocolate, Color.Chocolate); + yield return Brush(() => Brushes.Coral, Color.Coral); + yield return Brush(() => Brushes.CornflowerBlue, Color.CornflowerBlue); + yield return Brush(() => Brushes.Cornsilk, Color.Cornsilk); + yield return Brush(() => Brushes.Crimson, Color.Crimson); + yield return Brush(() => Brushes.Cyan, Color.Cyan); + yield return Brush(() => Brushes.DarkBlue, Color.DarkBlue); + yield return Brush(() => Brushes.DarkCyan, Color.DarkCyan); + yield return Brush(() => Brushes.DarkGoldenrod, Color.DarkGoldenrod); + yield return Brush(() => Brushes.DarkGray, Color.DarkGray); + yield return Brush(() => Brushes.DarkGreen, Color.DarkGreen); + yield return Brush(() => Brushes.DarkKhaki, Color.DarkKhaki); + yield return Brush(() => Brushes.DarkMagenta, Color.DarkMagenta); + yield return Brush(() => Brushes.DarkOliveGreen, Color.DarkOliveGreen); + yield return Brush(() => Brushes.DarkOrange, Color.DarkOrange); + yield return Brush(() => Brushes.DarkOrchid, Color.DarkOrchid); + yield return Brush(() => Brushes.DarkRed, Color.DarkRed); + yield return Brush(() => Brushes.DarkSalmon, Color.DarkSalmon); + yield return Brush(() => Brushes.DarkSeaGreen, Color.DarkSeaGreen); + yield return Brush(() => Brushes.DarkSlateBlue, Color.DarkSlateBlue); + yield return Brush(() => Brushes.DarkSlateGray, Color.DarkSlateGray); + yield return Brush(() => Brushes.DarkTurquoise, Color.DarkTurquoise); + yield return Brush(() => Brushes.DarkViolet, Color.DarkViolet); + yield return Brush(() => Brushes.DeepPink, Color.DeepPink); + yield return Brush(() => Brushes.DeepSkyBlue, Color.DeepSkyBlue); + yield return Brush(() => Brushes.DimGray, Color.DimGray); + yield return Brush(() => Brushes.DodgerBlue, Color.DodgerBlue); + yield return Brush(() => Brushes.Firebrick, Color.Firebrick); + yield return Brush(() => Brushes.FloralWhite, Color.FloralWhite); + yield return Brush(() => Brushes.ForestGreen, Color.ForestGreen); + yield return Brush(() => Brushes.Fuchsia, Color.Fuchsia); + yield return Brush(() => Brushes.Gainsboro, Color.Gainsboro); + yield return Brush(() => Brushes.GhostWhite, Color.GhostWhite); + yield return Brush(() => Brushes.Gold, Color.Gold); + yield return Brush(() => Brushes.Goldenrod, Color.Goldenrod); + yield return Brush(() => Brushes.Gray, Color.Gray); + yield return Brush(() => Brushes.Green, Color.Green); + yield return Brush(() => Brushes.GreenYellow, Color.GreenYellow); + yield return Brush(() => Brushes.Honeydew, Color.Honeydew); + yield return Brush(() => Brushes.HotPink, Color.HotPink); + yield return Brush(() => Brushes.IndianRed, Color.IndianRed); + yield return Brush(() => Brushes.Indigo, Color.Indigo); + yield return Brush(() => Brushes.Ivory, Color.Ivory); + yield return Brush(() => Brushes.Khaki, Color.Khaki); + yield return Brush(() => Brushes.Lavender, Color.Lavender); + yield return Brush(() => Brushes.LavenderBlush, Color.LavenderBlush); + yield return Brush(() => Brushes.LawnGreen, Color.LawnGreen); + yield return Brush(() => Brushes.LemonChiffon, Color.LemonChiffon); + yield return Brush(() => Brushes.LightBlue, Color.LightBlue); + yield return Brush(() => Brushes.LightCoral, Color.LightCoral); + yield return Brush(() => Brushes.LightCyan, Color.LightCyan); + yield return Brush(() => Brushes.LightGoldenrodYellow, Color.LightGoldenrodYellow); + yield return Brush(() => Brushes.LightGray, Color.LightGray); + yield return Brush(() => Brushes.LightGreen, Color.LightGreen); + yield return Brush(() => Brushes.LightPink, Color.LightPink); + yield return Brush(() => Brushes.LightSalmon, Color.LightSalmon); + yield return Brush(() => Brushes.LightSeaGreen, Color.LightSeaGreen); + yield return Brush(() => Brushes.LightSkyBlue, Color.LightSkyBlue); + yield return Brush(() => Brushes.LightSlateGray, Color.LightSlateGray); + yield return Brush(() => Brushes.LightSteelBlue, Color.LightSteelBlue); + yield return Brush(() => Brushes.LightYellow, Color.LightYellow); + yield return Brush(() => Brushes.Lime, Color.Lime); + yield return Brush(() => Brushes.LimeGreen, Color.LimeGreen); + yield return Brush(() => Brushes.Linen, Color.Linen); + yield return Brush(() => Brushes.Magenta, Color.Magenta); + yield return Brush(() => Brushes.Maroon, Color.Maroon); + yield return Brush(() => Brushes.MediumAquamarine, Color.MediumAquamarine); + yield return Brush(() => Brushes.MediumBlue, Color.MediumBlue); + yield return Brush(() => Brushes.MediumOrchid, Color.MediumOrchid); + yield return Brush(() => Brushes.MediumPurple, Color.MediumPurple); + yield return Brush(() => Brushes.MediumSeaGreen, Color.MediumSeaGreen); + yield return Brush(() => Brushes.MediumSlateBlue, Color.MediumSlateBlue); + yield return Brush(() => Brushes.MediumSpringGreen, Color.MediumSpringGreen); + yield return Brush(() => Brushes.MediumTurquoise, Color.MediumTurquoise); + yield return Brush(() => Brushes.MediumVioletRed, Color.MediumVioletRed); + yield return Brush(() => Brushes.MidnightBlue, Color.MidnightBlue); + yield return Brush(() => Brushes.MintCream, Color.MintCream); + yield return Brush(() => Brushes.MistyRose, Color.MistyRose); + yield return Brush(() => Brushes.Moccasin, Color.Moccasin); + yield return Brush(() => Brushes.NavajoWhite, Color.NavajoWhite); + yield return Brush(() => Brushes.Navy, Color.Navy); + yield return Brush(() => Brushes.OldLace, Color.OldLace); + yield return Brush(() => Brushes.Olive, Color.Olive); + yield return Brush(() => Brushes.OliveDrab, Color.OliveDrab); + yield return Brush(() => Brushes.Orange, Color.Orange); + yield return Brush(() => Brushes.OrangeRed, Color.OrangeRed); + yield return Brush(() => Brushes.Orchid, Color.Orchid); + yield return Brush(() => Brushes.PaleGoldenrod, Color.PaleGoldenrod); + yield return Brush(() => Brushes.PaleGreen, Color.PaleGreen); + yield return Brush(() => Brushes.PaleTurquoise, Color.PaleTurquoise); + yield return Brush(() => Brushes.PaleVioletRed, Color.PaleVioletRed); + yield return Brush(() => Brushes.PapayaWhip, Color.PapayaWhip); + yield return Brush(() => Brushes.PeachPuff, Color.PeachPuff); + yield return Brush(() => Brushes.Peru, Color.Peru); + yield return Brush(() => Brushes.Pink, Color.Pink); + yield return Brush(() => Brushes.Plum, Color.Plum); + yield return Brush(() => Brushes.PowderBlue, Color.PowderBlue); + yield return Brush(() => Brushes.Purple, Color.Purple); + yield return Brush(() => Brushes.Red, Color.Red); + yield return Brush(() => Brushes.RosyBrown, Color.RosyBrown); + yield return Brush(() => Brushes.RoyalBlue, Color.RoyalBlue); + yield return Brush(() => Brushes.SaddleBrown, Color.SaddleBrown); + yield return Brush(() => Brushes.Salmon, Color.Salmon); + yield return Brush(() => Brushes.SandyBrown, Color.SandyBrown); + yield return Brush(() => Brushes.SeaGreen, Color.SeaGreen); + yield return Brush(() => Brushes.SeaShell, Color.SeaShell); + yield return Brush(() => Brushes.Sienna, Color.Sienna); + yield return Brush(() => Brushes.Silver, Color.Silver); + yield return Brush(() => Brushes.SkyBlue, Color.SkyBlue); + yield return Brush(() => Brushes.SlateBlue, Color.SlateBlue); + yield return Brush(() => Brushes.SlateGray, Color.SlateGray); + yield return Brush(() => Brushes.Snow, Color.Snow); + yield return Brush(() => Brushes.SpringGreen, Color.SpringGreen); + yield return Brush(() => Brushes.SteelBlue, Color.SteelBlue); + yield return Brush(() => Brushes.Tan, Color.Tan); + yield return Brush(() => Brushes.Teal, Color.Teal); + yield return Brush(() => Brushes.Thistle, Color.Thistle); + yield return Brush(() => Brushes.Tomato, Color.Tomato); + yield return Brush(() => Brushes.Transparent, Color.Transparent); + yield return Brush(() => Brushes.Turquoise, Color.Turquoise); + yield return Brush(() => Brushes.Violet, Color.Violet); + yield return Brush(() => Brushes.Wheat, Color.Wheat); + yield return Brush(() => Brushes.White, Color.White); + yield return Brush(() => Brushes.WhiteSmoke, Color.WhiteSmoke); + yield return Brush(() => Brushes.Yellow, Color.Yellow); + yield return Brush(() => Brushes.YellowGreen, Color.YellowGreen); + } + + public static object[] Brush(Func getBrush, Color expectedColor) => new object[] { getBrush, expectedColor }; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Brushes_TestData))] + public void Brushes_Get_ReturnsExpected(Func getBrush, Color expectedColor) + { + SolidBrush brush = Assert.IsType(getBrush()); + Assert.Equal(expectedColor, brush.Color); + + Assert.Same(brush, getBrush()); + + // Brushes are not immutable. + Color color = brush.Color; + try + { + brush.Color = Color.Red; + Assert.Equal(Color.Red, brush.Color); + } + finally + { + brush.Color = color; + } + } + } +} diff --git a/src/System.Drawing.Common/tests/BufferedGraphicsContextTests.cs b/src/System.Drawing.Common/tests/BufferedGraphicsContextTests.cs new file mode 100644 index 00000000000..b8c7d27ab11 --- /dev/null +++ b/src/System.Drawing.Common/tests/BufferedGraphicsContextTests.cs @@ -0,0 +1,288 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.CompilerServices; +using Xunit; + +namespace System.Drawing.Tests +{ + public class BufferedGraphicsContextTests + { + [Fact] + public void Ctor_Default() + { + using (var context = new BufferedGraphicsContext()) + { + Assert.Equal(new Size(225, 96), context.MaximumBuffer); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_ValidTargetGraphics_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (BufferedGraphics bufferedGraphics = context.Allocate(graphics, Rectangle.Empty)) + { + Assert.NotNull(bufferedGraphics.Graphics); + + context.Invalidate(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_SmallRectWithTargetGraphics_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (BufferedGraphics bufferedGraphics = context.Allocate(graphics, new Rectangle(0, 0, context.MaximumBuffer.Width - 1, context.MaximumBuffer.Height - 1))) + { + Assert.NotNull(bufferedGraphics.Graphics); + + context.Invalidate(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_LargeRectWithTargetGraphics_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (BufferedGraphics bufferedGraphics = context.Allocate(graphics, new Rectangle(0, 0, context.MaximumBuffer.Width + 1, context.MaximumBuffer.Height + 1))) + { + Assert.NotNull(bufferedGraphics.Graphics); + + context.Invalidate(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_ValidTargetHdc_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + try + { + IntPtr hdc = graphics.GetHdc(); + using (BufferedGraphics bufferedGraphics = context.Allocate(hdc, Rectangle.Empty)) + { + Assert.NotNull(bufferedGraphics.Graphics); + } + + context.Invalidate(); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_SmallRectWithTargetHdc_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + try + { + IntPtr hdc = graphics.GetHdc(); + using (BufferedGraphics bufferedGraphics = context.Allocate(hdc, new Rectangle(0, 0, context.MaximumBuffer.Width - 1, context.MaximumBuffer.Height - 1))) + { + Assert.NotNull(bufferedGraphics.Graphics); + } + + context.Invalidate(); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_LargeRectWithTargetHdc_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + try + { + IntPtr hdc = graphics.GetHdc(); + using (BufferedGraphics bufferedGraphics = context.Allocate(hdc, new Rectangle(0, 0, context.MaximumBuffer.Width + 1, context.MaximumBuffer.Height + 1))) + { + Assert.NotNull(bufferedGraphics.Graphics); + } + + context.Invalidate(); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [Fact] + public void Allocate_InvalidHdc_ThrowsArgumentException() + { + using (var context = new BufferedGraphicsContext()) + { + AssertExtensions.Throws(null, () => context.Allocate((IntPtr)(-1), new Rectangle(0, 0, 10, 10))); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_NullGraphicsZeroSize_Success() + { + using (var context = new BufferedGraphicsContext()) + using (BufferedGraphics graphics = context.Allocate(null, Rectangle.Empty)) + { + Assert.NotNull(graphics.Graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_NullGraphicsNonZeroSize_ThrowsArgumentNullException() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + { + Assert.Throws("hdc", () => context.Allocate(null, new Rectangle(0, 0, 10, 10))); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_DisposedGraphics_ThrowsArgumentException() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + Rectangle largeRectangle = new Rectangle(0, 0, context.MaximumBuffer.Width + 1, context.MaximumBuffer.Height + 1); + AssertExtensions.Throws(null, () => context.Allocate(graphics, largeRectangle)); + AssertExtensions.Throws(null, () => context.Allocate(graphics, Rectangle.Empty)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Allocate_BusyGraphics_ThrowsInvalidOperationException() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + try + { + graphics.GetHdc(); + + Rectangle largeRectangle = new Rectangle(0, 0, context.MaximumBuffer.Width + 1, context.MaximumBuffer.Height + 1); + Assert.Throws(() => context.Allocate(graphics, largeRectangle)); + Assert.Throws(() => context.Allocate(graphics, Rectangle.Empty)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [Fact] + public void Invalidate_CallMultipleTimes_Success() + { + using (var context = new BufferedGraphicsContext()) + { + context.Invalidate(); + context.Invalidate(); + } + } + + [Fact] + public void MaximumBuffer_SetValid_ReturnsExpected() + { + using (var context = new BufferedGraphicsContext()) + { + context.MaximumBuffer = new Size(10, 10); + Assert.Equal(new Size(10, 10), context.MaximumBuffer); + + context.MaximumBuffer = new Size(255, 255); + Assert.Equal(new Size(255, 255), context.MaximumBuffer); + } + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void MaximumBuffer_SetInvalidWidth_ThrowsArgumentException(int width) + { + using (var context = new BufferedGraphicsContext()) + { + AssertExtensions.Throws("value", null, () => context.MaximumBuffer = new Size(width, 1)); + } + } + + [Theory] + [InlineData(0)] + [InlineData(-1)] + public void MaximumBuffer_SetInvalidHeight_ThrowsArgumentException(int height) + { + using (var context = new BufferedGraphicsContext()) + { + AssertExtensions.Throws("value", null, () => context.MaximumBuffer = new Size(1, height)); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void AllocateBufferedGraphicsContext() => new BufferedGraphicsContext(); + + + [Fact] + public void Finalize_Invoke_Success() + { + // This makes sure than finalization doesn't cause any errors or debug assertions. + AllocateBufferedGraphicsContext(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_BusyAndValidated_ThrowsInvalidOperationException() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + using (context.Allocate(graphics, Rectangle.Empty)) + { + Assert.Throws(() => context.Dispose()); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_BusyAndInvalidated_ThrowsInvalidOperationException() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + using (context.Allocate(graphics, Rectangle.Empty)) + { + context.Invalidate(); + Assert.Throws(() => context.Dispose()); + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/BufferedGraphicsManagerTests.cs b/src/System.Drawing.Common/tests/BufferedGraphicsManagerTests.cs new file mode 100644 index 00000000000..27ed7f6f8fc --- /dev/null +++ b/src/System.Drawing.Common/tests/BufferedGraphicsManagerTests.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class BufferedGraphicsManagerTests + { + [Fact] + public void Current_Get_ReturnsSameInstance() + { + Assert.Same(BufferedGraphicsManager.Current, BufferedGraphicsManager.Current); + Assert.NotNull(BufferedGraphicsManager.Current); + } + } +} diff --git a/src/System.Drawing.Common/tests/BufferedGraphicsTests.cs b/src/System.Drawing.Common/tests/BufferedGraphicsTests.cs new file mode 100644 index 00000000000..ba2bd821ce7 --- /dev/null +++ b/src/System.Drawing.Common/tests/BufferedGraphicsTests.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class BufferedGraphicsTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_TempMultipleTimes_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(3, 3)) + using (Graphics targetGraphics = Graphics.FromImage(image)) + { + BufferedGraphics graphics = context.Allocate(targetGraphics, new Rectangle(0, 0, 1, 1)); + Assert.NotNull(graphics.Graphics); + + graphics.Dispose(); + Assert.Null(graphics.Graphics); + + graphics.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_ActualMultipleTimes_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(3, 3)) + using (Graphics targetGraphics = Graphics.FromImage(image)) + { + BufferedGraphics graphics = context.Allocate(targetGraphics, new Rectangle(0, 0, context.MaximumBuffer.Width + 1, context.MaximumBuffer.Height + 1)); + Assert.NotNull(graphics.Graphics); + + graphics.Dispose(); + Assert.Null(graphics.Graphics); + + graphics.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Render_ParameterlessWithTargetGraphics_Success() + { + Color color = Color.FromArgb(255, 0, 0, 0); + + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + graphics.FillRectangle(brush, new Rectangle(0, 0, 3, 3)); + + using (BufferedGraphics bufferedGraphics = context.Allocate(graphics, new Rectangle(0, 0, 3, 3))) + { + bufferedGraphics.Render(); + + Helpers.VerifyBitmap(image, new Color[][] + { + new Color[] { color, color, color }, + new Color[] { color, color, color }, + new Color[] { color, color, color } + }); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Render_ParameterlessWithNullTargetGraphics_Success() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + graphics.FillRectangle(brush, new Rectangle(0, 0, 3, 3)); + try + { + IntPtr hdc = graphics.GetHdc(); + + using (BufferedGraphics bufferedGraphics = context.Allocate(hdc, new Rectangle(0, 0, 3, 3))) + { + bufferedGraphics.Render(); + } + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Render_TargetGraphics_Success() + { + Color color = Color.FromArgb(255, 0, 0, 0); + + using (var context = new BufferedGraphicsContext()) + using (var originalImage = new Bitmap(3, 3)) + using (var targetImage = new Bitmap(3, 3)) + using (Graphics originalGraphics = Graphics.FromImage(originalImage)) + using (Graphics targetGraphics = Graphics.FromImage(targetImage)) + using (var brush = new SolidBrush(Color.Red)) + { + originalGraphics.FillRectangle(brush, new Rectangle(0, 0, 3, 3)); + + using (BufferedGraphics graphics = context.Allocate(originalGraphics, new Rectangle(0, 0, 3, 3))) + { + graphics.Render(targetGraphics); + + Helpers.VerifyBitmap(targetImage, new Color[][] + { + new Color[] { color, color, color }, + new Color[] { color, color, color }, + new Color[] { color, color, color } + }); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Render_NullGraphics_Nop() + { + using (var context = new BufferedGraphicsContext()) + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + using (BufferedGraphics bufferedGraphics = context.Allocate(graphics, new Rectangle(0, 0, 1, 1))) + { + bufferedGraphics.Render(null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Render_InvalidTargetDC_Nop() + { + using (var context = new BufferedGraphicsContext()) + using (BufferedGraphics graphics = context.Allocate(null, Rectangle.Empty)) + { + graphics.Render(IntPtr.Zero); + graphics.Render((IntPtr)(-1)); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/CharacterRangeTests.cs b/src/System.Drawing.Common/tests/CharacterRangeTests.cs new file mode 100644 index 00000000000..989ee85c093 --- /dev/null +++ b/src/System.Drawing.Common/tests/CharacterRangeTests.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Text; +using Xunit; + +namespace System.Drawing.Tests +{ + public class CharacterRangeTests + { + [Fact] + public void Ctor_Default() + { + var range = new CharacterRange(); + Assert.Equal(0, range.First); + Assert.Equal(0, range.Length); + } + + [Theory] + [InlineData(-1, -2)] + [InlineData(0, 0)] + [InlineData(1, 2)] + public void Ctor_Int_Int(int First, int Length) + { + var range = new CharacterRange(First, Length); + Assert.Equal(First, range.First); + Assert.Equal(Length, range.Length); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(10)] + public void First_Set_GetReturnsExpected(int value) + { + var range = new CharacterRange + { + First = value + }; + Assert.Equal(value, range.First); + + // Set same. + range.First = value; + Assert.Equal(value, range.First); + } + + [Theory] + [InlineData(-1)] + [InlineData(0)] + [InlineData(10)] + public void Length_Set_GetReturnsExpected(int value) + { + var range = new CharacterRange + { + Length = value + }; + Assert.Equal(value, range.Length); + + // Set same. + range.Length = value; + Assert.Equal(value, range.Length); + } + + public static IEnumerable Equals_TestData() + { + yield return new object[] { new CharacterRange(1, 2), new CharacterRange(1, 2), true }; + yield return new object[] { new CharacterRange(1, 2), new CharacterRange(2, 2), false }; + yield return new object[] { new CharacterRange(1, 2), new CharacterRange(1, 1), false }; + yield return new object[] { new CharacterRange(1, 2), new object(), false }; + + // .NET Framework throws NullReferenceException. + if (!PlatformDetection.IsNetFramework) + { + yield return new object[] { new CharacterRange(1, 2), null, false }; + } + } + + [Theory] + [MemberData(nameof(Equals_TestData))] + public void Equals_Invoke_ReturnsExpected(CharacterRange range, object obj, bool expected) + { + Assert.Equal(expected, range.Equals(obj)); + if (obj is CharacterRange otherRange) + { + Assert.Equal(expected, range.Equals(otherRange)); + Assert.Equal(expected, range == otherRange); + Assert.Equal(!expected, range != otherRange); + Assert.Equal(expected, range.GetHashCode().Equals(otherRange.GetHashCode())); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/ColorTranslatorTests.cs b/src/System.Drawing.Common/tests/ColorTranslatorTests.cs new file mode 100644 index 00000000000..fcb6ecde122 --- /dev/null +++ b/src/System.Drawing.Common/tests/ColorTranslatorTests.cs @@ -0,0 +1,266 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Threading; +using Xunit; + +namespace System.Drawing.Tests +{ + public class ColorTranslatorTests + { + public static IEnumerable<(int, Color)> SystemColors_TestData() + { + yield return (unchecked((int)0x8000000A), SystemColors.ActiveBorder); + yield return (unchecked((int)0x80000002), SystemColors.ActiveCaption); + yield return (unchecked((int)0x80000009), SystemColors.ActiveCaptionText); + yield return (unchecked((int)0x8000000C), SystemColors.AppWorkspace); + yield return (unchecked((int)0x8000000F), SystemColors.Control); + yield return (unchecked((int)0x80000010), SystemColors.ControlDark); + yield return (unchecked((int)0x80000015), SystemColors.ControlDarkDark); + yield return (unchecked((int)0x80000016), SystemColors.ControlLight); + yield return (unchecked((int)0x80000014), SystemColors.ControlLightLight); + yield return (unchecked((int)0x80000012), SystemColors.ControlText); + yield return (unchecked((int)0x80000001), SystemColors.Desktop); + yield return (unchecked((int)0x8000001B), SystemColors.GradientActiveCaption); + yield return (unchecked((int)0x8000001C), SystemColors.GradientInactiveCaption); + yield return (unchecked((int)0x80000011), SystemColors.GrayText); + yield return (unchecked((int)0x8000000D), SystemColors.Highlight); + yield return (unchecked((int)0x8000000E), SystemColors.HighlightText); + yield return (unchecked((int)0x8000001A), SystemColors.HotTrack); + yield return (unchecked((int)0x8000000B), SystemColors.InactiveBorder); + yield return (unchecked((int)0x80000003), SystemColors.InactiveCaption); + yield return (unchecked((int)0x80000013), SystemColors.InactiveCaptionText); + yield return (unchecked((int)0x80000018), SystemColors.Info); + yield return (unchecked((int)0x80000017), SystemColors.InfoText); + yield return (unchecked((int)0x80000004), SystemColors.Menu); + yield return (unchecked((int)0x8000001E), SystemColors.MenuBar); + yield return (unchecked((int)0x8000001D), SystemColors.MenuHighlight); + yield return (unchecked((int)0x80000007), SystemColors.MenuText); + yield return (unchecked((int)0x80000000), SystemColors.ScrollBar); + yield return (unchecked((int)0x80000005), SystemColors.Window); + yield return (unchecked((int)0x80000006), SystemColors.WindowFrame); + yield return (unchecked((int)0x80000008), SystemColors.WindowText); + } + + public static IEnumerable ToWin32Color_TestData() + { + yield return new object[] { new Color(), 0 }; + yield return new object[] { Color.Red, 255 }; + yield return new object[] { Color.White, 16777215 }; + yield return new object[] { Color.FromArgb(1, 2, 3), 197121 }; + } + + [Theory] + [MemberData(nameof(ToWin32Color_TestData))] + public void ToWin32Color_Color_ReturnsExpected(Color color, int expected) + { + Assert.Equal(expected, ColorTranslator.ToWin32(color)); + } + + public static IEnumerable FromOle_TestData() + { + yield return new object[] { int.MinValue, SystemColors.ScrollBar }; + yield return new object[] { -1, Color.White }; + yield return new object[] { 0, Color.Black }; + yield return new object[] { 197121, Color.FromArgb(1, 2, 3) }; + yield return new object[] { 16777215, Color.White }; + yield return new object[] { int.MaxValue, Color.White }; + yield return new object[] { unchecked((int)0x8000001F), Color.FromArgb(255, 31, 0, 0) }; + yield return new object[] { unchecked((int)0x80000019), Color.FromArgb(255, 25, 0, 0) }; + + foreach ((int oleColor, Color color) in SystemColors_TestData()) + { + yield return new object[] { oleColor, color }; + } + } + + [Theory] + [MemberData(nameof(FromOle_TestData))] + public void FromOle_Color_ReturnsExpected(int oleColor, Color color) + { + Assert.Equal(color, ColorTranslator.FromOle(oleColor)); + Assert.Equal(color, ColorTranslator.FromWin32(oleColor)); + } + + public static IEnumerable ToOle_TestData() + { + yield return new object[] { new Color(), 0 }; + yield return new object[] { Color.Red, 255 }; + yield return new object[] { Color.White, 16777215 }; + yield return new object[] { Color.FromArgb(1, 2, 3), 197121 }; + + foreach ((int oleColor, Color color) in SystemColors_TestData()) + { + yield return new object[] { color, oleColor }; + } + + + // These system colors are equivalent to Control, ControlLight and ControlDark. + yield return new object[] { SystemColors.ButtonFace, unchecked((int)0x8000000F) }; + yield return new object[] { SystemColors.ButtonHighlight, unchecked((int)0x80000014) }; + yield return new object[] { SystemColors.ButtonShadow, unchecked((int)0x80000010) }; + } + + [Theory] + [MemberData(nameof(ToOle_TestData))] + public void ToOle_Color_ReturnsExpected(Color color, int oleColor) + { + Assert.Equal(oleColor, ColorTranslator.ToOle(color)); + } + + public static IEnumerable<(string, Color)> HtmlColors_TestData() + { + yield return ("activeborder", SystemColors.ActiveBorder); + yield return ("activecaption", SystemColors.ActiveCaption); + yield return ("appworkspace", SystemColors.AppWorkspace); + yield return ("background", SystemColors.Desktop); + yield return ("buttonface", SystemColors.Control); + yield return ("buttonhighlight", SystemColors.ControlLightLight); + yield return ("buttonshadow", SystemColors.ControlDark); + yield return ("buttontext", SystemColors.ControlText); + yield return ("captiontext", SystemColors.ActiveCaptionText); + yield return ("graytext", SystemColors.GrayText); + yield return ("highlight", SystemColors.Highlight); + yield return ("highlighttext", SystemColors.HighlightText); + yield return ("inactiveborder", SystemColors.InactiveBorder); + yield return ("inactivecaption", SystemColors.InactiveCaption); + yield return ("inactivecaptiontext", SystemColors.InactiveCaptionText); + yield return ("infobackground", SystemColors.Info); + yield return ("infotext", SystemColors.InfoText); + yield return ("menu", SystemColors.Menu); + yield return ("menutext", SystemColors.MenuText); + yield return ("scrollbar", SystemColors.ScrollBar); + yield return ("window", SystemColors.Window); + yield return ("windowframe", SystemColors.WindowFrame); + yield return ("windowtext", SystemColors.WindowText); + yield return ("threeddarkshadow", SystemColors.ControlDarkDark); + + yield return ("LightGrey", Color.LightGray); + yield return ("Blue", Color.Blue); + yield return ("#1F2E3D", Color.FromArgb(31, 46, 61)); + } + + public static IEnumerable FromHtml_TestData() + { + yield return new object[] { null, Color.Empty }; + yield return new object[] { "", Color.Empty }; + yield return new object[] { " ", Color.Empty }; + yield return new object[] { "''", Color.FromName("") }; + yield return new object[] { "\"\"", Color.FromName("") }; + + yield return new object[] { "#1B3", Color.FromArgb(17, 187, 51) }; + yield return new object[] { " #1F2E3D ", Color.FromArgb(31, 46, 61) }; + + yield return new object[] { "ActiveBorder", SystemColors.ActiveBorder }; + yield return new object[] { "ACTIVEBORDER", SystemColors.ActiveBorder }; + yield return new object[] { " Blue ", Color.Blue }; + yield return new object[] { "'Blue'", Color.Blue }; + yield return new object[] { "\"Blue\"", Color.Blue }; + yield return new object[] { "'None'", Color.FromName("None") }; + yield return new object[] { "\"None\"", Color.FromName("None") }; + yield return new object[] { "255,0,0", Color.Red }; + + // Color(argb) + yield return new object[] { 498, Color.FromArgb(0, 0, 1, 242) }; + yield return new object[] { "&h1F2", Color.FromArgb(0, 0, 1, 242) }; + yield return new object[] { "&h1F2", Color.FromArgb(0, 0, 1, 242) }; + + // Color(red, green, blue) + yield return new object[] { "1, 0x2, &h3", Color.FromArgb(1, 2, 3) }; + + // Color(alpha, red, green, blue) + yield return new object[] { "1, 2, 0x3, &h4", Color.FromArgb(1, 2, 3, 4) }; + + foreach ((string htmlColor, Color color) in HtmlColors_TestData()) + { + yield return new object[] { htmlColor, color }; + } + + // Some of the SystemColors.Control colors don't roundtrip. + yield return new object[] { "threedface", SystemColors.Control }; + yield return new object[] { "threedhighlight", SystemColors.ControlLight }; + yield return new object[] { "threedlightshadow", SystemColors.ControlLightLight }; + } + + [Theory] + [MemberData(nameof(FromHtml_TestData))] + public void FromHtml_String_ReturnsExpected(string htmlColor, Color expected) + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture)) + { + Assert.Equal(expected, ColorTranslator.FromHtml(htmlColor)); + } + } + + [Theory] + [InlineData("'")] + [InlineData("'\"")] + [InlineData("\"'")] + [InlineData("#")] + [InlineData(" #G12 ")] + [InlineData(" #G12345 ")] + [InlineData("#FFFFFFFFF")] + [InlineData("0x")] + [InlineData("0xFFFFFFFFF")] + [InlineData("0xG12")] + [InlineData("&h")] + [InlineData("&hG12")] + public void FromHtml_Invalid_Throws(string htmlColor) + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture)) + { + Exception exception = AssertExtensions.Throws(() => ColorTranslator.FromHtml(htmlColor)); + if (exception is ArgumentException argumentException) + Assert.Equal("htmlColor", argumentException.ParamName); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("#G12", typeof(FormatException))] + [InlineData("#G12345", typeof(FormatException))] + [InlineData("1,2", typeof(ArgumentException))] + [InlineData("1,2,3,4,5", typeof(ArgumentException))] + [InlineData("-1,2,3", typeof(ArgumentException))] + [InlineData("256,2,3", typeof(ArgumentException))] + [InlineData("1,-1,3", typeof(ArgumentException))] + [InlineData("1,256,3", typeof(ArgumentException))] + [InlineData("1,2,-1", typeof(ArgumentException))] + [InlineData("1,2,256", typeof(ArgumentException))] + public void FromHtml_Invalid_Throw(string htmlColor, Type exception) + { + using (new ThreadCultureChange(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture)) + { + Assert.Throws(exception, () => ColorTranslator.FromHtml(htmlColor)); + } + } + + public static IEnumerable ToHtml_TestData() + { + yield return new object[] { Color.Empty, "" }; + + foreach ((string htmlColor, Color color) in HtmlColors_TestData()) + { + yield return new object[] { color, htmlColor }; + } + + // SystemColors.ControlLight don't roundtrip. + yield return new object[] { SystemColors.ControlLight, "buttonface" }; + yield return new object[] { SystemColors.GradientActiveCaption, "activecaption" }; + yield return new object[] { SystemColors.HotTrack, "highlight" }; + yield return new object[] { SystemColors.MenuHighlight, "highlighttext" }; + yield return new object[] { SystemColors.GradientInactiveCaption, "inactivecaption" }; + yield return new object[] { SystemColors.MenuBar, "menu" }; + yield return new object[] { SystemColors.ButtonShadow, "" }; + } + + [Theory] + [MemberData(nameof(ToHtml_TestData))] + public void ToHtml_Color_ReturnsExpected(Color color, string expected) + { + Assert.Equal(expected, ColorTranslator.ToHtml(color)); + } + } +} diff --git a/src/System.Drawing.Common/tests/Design/CategoryNameCollectionTests.cs b/src/System.Drawing.Common/tests/Design/CategoryNameCollectionTests.cs new file mode 100644 index 00000000000..f99d698011b --- /dev/null +++ b/src/System.Drawing.Common/tests/Design/CategoryNameCollectionTests.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Linq; +using Xunit; + +namespace System.Drawing.Design.Tests +{ + public class CategoryNameCollectionTests + { + [Fact] + public void Ctor_StringArray() + { + var value = new string[] { "1", "2", "3" }; + var collection = new CategoryNameCollection(value); + Assert.Equal(value, collection.Cast()); + } + + [Fact] + public void Ctor_CategoryNameCollection() + { + var value = new string[] { "1", "2", "3" }; + var sourceCollection = new CategoryNameCollection(value); + + var collection = new CategoryNameCollection(sourceCollection); + Assert.Equal(value, collection.Cast()); + } + + [Fact] + public void Indexer_Get_ReturnsExpected() + { + var value = new string[] { "1", "2", "3" }; + var sourceCollection = new CategoryNameCollection(value); + + for (int i = 0; i < sourceCollection.Count; i++) + { + string expectedValue = value[i]; + Assert.Equal(expectedValue, sourceCollection[i]); + Assert.True(sourceCollection.Contains(expectedValue)); + Assert.Equal(i, sourceCollection.IndexOf(expectedValue)); + } + } + + [Fact] + public void CopyTo_Valid_Success() + { + var value = new string[] { "1", "2", "3" }; + var sourceCollection = new CategoryNameCollection(value); + + var destination = new string[5]; + sourceCollection.CopyTo(destination, 1); + + Assert.Equal(new string[] { null, "1", "2", "3", null }, destination); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/AdjustableArrowCapTests.cs b/src/System.Drawing.Common/tests/Drawing2D/AdjustableArrowCapTests.cs new file mode 100644 index 00000000000..17f6e41ecb1 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/AdjustableArrowCapTests.cs @@ -0,0 +1,139 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class AdjustableArrowCapTests + { + public static IEnumerable Ctor_Float_Float_TestData() + { + yield return new object[] { 1f, 1f }; + yield return new object[] { 50f, 50f }; + yield return new object[] { float.MaxValue, float.MaxValue }; + // Nonsensical values -- but still permitted. + yield return new object[] { -1f, 1f }; + yield return new object[] { float.PositiveInfinity, 1f }; + yield return new object[] { float.NegativeInfinity, 1f }; + yield return new object[] { float.NaN, 1f }; + yield return new object[] { 0f, 1f }; + yield return new object[] { 0f, 0f }; + yield return new object[] { 1f, -1f }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Float_Float_TestData))] + public void Ctor_Float_Float(float width, float height) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(width, height)) + { + Assert.Equal(width, arrowCap.Width); + Assert.Equal(height, arrowCap.Height); + Assert.True(arrowCap.Filled); + } + } + + public static IEnumerable Ctor_Float_Float_Bool_TestData() + { + foreach (object[] data in Ctor_Float_Float_TestData()) + { + yield return new object[] { data[0], data[1], true }; + yield return new object[] { data[0], data[1], false }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Float_Float_Bool_TestData))] + public void Ctor_Float_Float_Bool(float width, float height, bool filled) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(width, height, filled)) + { + Assert.Equal(width, arrowCap.Width); + Assert.Equal(height, arrowCap.Height); + Assert.Equal(filled, arrowCap.Filled); + } + } + + public static IEnumerable Properties_TestData() + { + yield return new object[] { -1 }; + yield return new object[] { 0 }; + yield return new object[] { 10 }; + yield return new object[] { 5000 }; + yield return new object[] { float.MaxValue }; + yield return new object[] { float.PositiveInfinity }; + yield return new object[] { float.NegativeInfinity }; + yield return new object[] { float.NaN }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Properties_TestData))] + public void Width_Set_GetReturnsExpected(float width) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + { + arrowCap.Width = width; + Assert.Equal(width, arrowCap.Width); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Properties_TestData))] + public void Height_Set_GetReturnsExpected(float height) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + { + arrowCap.Height = height; + Assert.Equal(height, arrowCap.Height); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Properties_TestData))] + public void MiddleInset_Set_GetReturnsExpected(float middleInset) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + { + arrowCap.MiddleInset = middleInset; + Assert.Equal(middleInset, arrowCap.MiddleInset); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(true)] + [InlineData(false)] + public void Filled_Set_GetReturnsExpected(bool filled) + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + { + arrowCap.Filled = filled; + Assert.Equal(filled, arrowCap.Filled); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Success() + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + using (AdjustableArrowCap clone = Assert.IsType(arrowCap.Clone())) + { + Assert.NotSame(clone, arrowCap); + Assert.Equal(clone.Width, arrowCap.Width); + Assert.Equal(clone.Height, arrowCap.Height); + Assert.Equal(clone.MiddleInset, arrowCap.MiddleInset); + Assert.Equal(clone.Filled, arrowCap.Filled); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void BaseCap_ReturnsTriangle() + { + using (AdjustableArrowCap arrowCap = new AdjustableArrowCap(1, 1)) + { + Assert.Equal(LineCap.Triangle, arrowCap.BaseCap); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/BlendTests.cs b/src/System.Drawing.Common/tests/Drawing2D/BlendTests.cs new file mode 100644 index 00000000000..e9c3681e215 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/BlendTests.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class BlendTests + { + [Fact] + public void Ctor_Default() + { + var blend = new Blend(); + Assert.Equal(new float[1], blend.Factors); + Assert.Equal(new float[1], blend.Positions); + } + + [Theory] + [InlineData(0)] + [InlineData(2)] + public void Ctor_Count(int count) + { + var blend = new Blend(count); + Assert.Equal(new float[count], blend.Factors); + Assert.Equal(new float[count], blend.Positions); + } + + [Fact] + public void Ctor_InvalidCount_ThrowsOverflowException() + { + Assert.Throws(() => new Blend(-1)); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotIntMaxValueArrayIndexSupported))] + public void Ctor_LargeCount_ThrowsOutOfMemoryException() + { + Assert.Throws(() => new Blend(int.MaxValue)); + } + + [Fact] + public void Factors_Set_GetReturnsExpected() + { + var blend = new Blend { Factors = null }; + Assert.Null(blend.Factors); + + blend.Factors = new float[10]; + Assert.Equal(new float[10], blend.Factors); + } + + [Fact] + public void Positions_Set_GetReturnsExpected() + { + var blend = new Blend { Positions = null }; + Assert.Null(blend.Positions); + + blend.Positions = new float[10]; + Assert.Equal(new float[10], blend.Positions); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/ColorBlendTests.cs b/src/System.Drawing.Common/tests/Drawing2D/ColorBlendTests.cs new file mode 100644 index 00000000000..2f2a54c275a --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/ColorBlendTests.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class ColorBlendTests + { + [Fact] + public void Ctor_Default() + { + var blend = new ColorBlend(); + Assert.Equal(new Color[1], blend.Colors); + Assert.Equal(new float[1], blend.Positions); + } + + [Theory] + [InlineData(0)] + [InlineData(2)] + public void Ctor_Count(int count) + { + var blend = new ColorBlend(count); + Assert.Equal(new Color[count], blend.Colors); + Assert.Equal(new float[count], blend.Positions); + } + + [Fact] + public void Ctor_InvalidCount_ThrowsOverflowException() + { + Assert.Throws(() => new ColorBlend(-1)); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotIntMaxValueArrayIndexSupported))] + public void Ctor_LargeCount_ThrowsOutOfMemoryException() + { + Assert.Throws(() => new ColorBlend(int.MaxValue)); + } + + [Fact] + public void Colors_Set_GetReturnsExpected() + { + var blend = new ColorBlend { Colors = null }; + Assert.Null(blend.Colors); + + blend.Colors = new Color[10]; + Assert.Equal(new Color[10], blend.Colors); + } + + [Fact] + public void Positions_Set_GetReturnsExpected() + { + var blend = new ColorBlend { Positions = null }; + Assert.Null(blend.Positions); + + blend.Positions = new float[10]; + Assert.Equal(new float[10], blend.Positions); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/CustomLineCapTests.cs b/src/System.Drawing.Common/tests/Drawing2D/CustomLineCapTests.cs new file mode 100644 index 00000000000..17ca9acea9f --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/CustomLineCapTests.cs @@ -0,0 +1,247 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class CustomLineCapTests + { + public static IEnumerable Ctor_Path_Path_LineCap_Float_TestData() + { + yield return new object[] { new GraphicsPath(), null, LineCap.Flat, 0f, LineCap.Flat }; + yield return new object[] { new GraphicsPath(), null, LineCap.Square, 1f, LineCap.Square }; + yield return new object[] { new GraphicsPath(), null, LineCap.Round, -1f, LineCap.Round }; + yield return new object[] { new GraphicsPath(), null, LineCap.Triangle, float.MaxValue, LineCap.Triangle }; + // All of these "anchor" values yield a "Flat" LineCap. + yield return new object[] { new GraphicsPath(), null, LineCap.NoAnchor, 0f, LineCap.Flat }; + yield return new object[] { new GraphicsPath(), null, LineCap.SquareAnchor, 0f, LineCap.Flat }; + yield return new object[] { new GraphicsPath(), null, LineCap.DiamondAnchor, 0f, LineCap.Flat }; + yield return new object[] { new GraphicsPath(), null, LineCap.ArrowAnchor, 0f, LineCap.Flat }; + + // Boxy cap + GraphicsPath strokePath = new GraphicsPath(); + strokePath.AddRectangle(new Rectangle(0, 0, 10, 10)); + yield return new object[] { null, strokePath, LineCap.Square, 0f, LineCap.Square }; + + // Hook-shaped cap + strokePath = new GraphicsPath(); + strokePath.AddLine(new Point(0, 0), new Point(0, 5)); + strokePath.AddLine(new Point(0, 5), new Point(5, 1)); + strokePath.AddLine(new Point(5, 1), new Point(3, 1)); + yield return new object[] { null, strokePath, LineCap.Flat, 0f, LineCap.Flat }; + + // Fill path -- Must intercept the Y-axis. + GraphicsPath fillPath = new GraphicsPath(); + fillPath.AddLine(new Point(-5, -10), new Point(0, 10)); + fillPath.AddLine(new Point(0, 10), new Point(5, -10)); + fillPath.AddLine(new Point(5, -10), new Point(-5, -10)); + yield return new object[] { fillPath, null, LineCap.Flat, 0f, LineCap.Flat }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Ctor_Path_Path_LineCap_Float_TestData))] + public void Ctor_Path_Path_LineCap_Float(GraphicsPath fillPath, GraphicsPath strokePath, LineCap baseCap, float baseInset, LineCap expectedCap) + { + using (fillPath) + using (strokePath) + using (CustomLineCap customLineCap = new CustomLineCap(fillPath, strokePath, baseCap, baseInset)) + { + Assert.Equal(expectedCap, customLineCap.BaseCap); + Assert.Equal(baseInset, customLineCap.BaseInset); + Assert.Equal(LineJoin.Miter, customLineCap.StrokeJoin); + Assert.Equal(1f, customLineCap.WidthScale); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + // These values are outside the valid range of the LineCap enum. + [InlineData(LineCap.Flat - 1)] + [InlineData(LineCap.Custom + 1)] + public void Ctor_InvalidLineCap_ReturnsFlat(LineCap baseCap) + { + using (GraphicsPath fillPath = new GraphicsPath()) + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(fillPath, strokePath, baseCap, 0f)) + { + Assert.Equal(LineCap.Flat, customLineCap.BaseCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_FillPath_Incomplete_ThrowsArgumentException() + { + using (GraphicsPath fillPath = new GraphicsPath()) + { + fillPath.AddLine(new Point(0, -10), new Point(0, 10)); + AssertExtensions.Throws(null, () => new CustomLineCap(fillPath, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_FillPath_DoesNotCrossYAxis_ThrowsNotImplementedException() + { + // Closed fillPath, but does not cross the Y-axis. + using (GraphicsPath fillPath = new GraphicsPath()) + { + fillPath.AddLine(new Point(-5, 5), new Point(5, 5)); + fillPath.AddLine(new Point(5, 5), new Point(5, 1)); + fillPath.AddLine(new Point(5, 1), new Point(-5, 5)); + Assert.Throws(() => new CustomLineCap(fillPath, null)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineCap.Square, LineCap.Square)] + [InlineData(LineCap.Round, LineCap.Round)] + [InlineData(LineCap.Triangle, LineCap.Triangle)] + public void SetThenGetStrokeCaps_Success(LineCap startCap, LineCap endCap) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.SetStrokeCaps(startCap, endCap); + customLineCap.GetStrokeCaps(out LineCap retrievedStartCap, out LineCap retrievedEndCap); + + Assert.Equal(startCap, retrievedStartCap); + Assert.Equal(endCap, retrievedEndCap); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(LineCap.SquareAnchor, LineCap.SquareAnchor)] + [InlineData(LineCap.Custom, LineCap.Custom)] + [InlineData(LineCap.Square, LineCap.Custom)] + [InlineData(LineCap.Custom, LineCap.SquareAnchor)] + [InlineData(LineCap.Flat - 1, LineCap.Flat)] // Below valid enum range + [InlineData(LineCap.Custom + 1, LineCap.Flat)] // Above valid enum range + public void SetStrokeCaps_InvalidLineCap_ThrowsArgumentException(LineCap startCap, LineCap endCap) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + AssertExtensions.Throws(null, () => customLineCap.SetStrokeCaps(startCap, endCap)); + + // start and end cap should be unchanged. + customLineCap.GetStrokeCaps(out LineCap retrievedStartCap, out LineCap retrievedEndCap); + Assert.Equal(LineCap.Flat, retrievedStartCap); + Assert.Equal(LineCap.Flat, retrievedEndCap); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineJoin.Miter)] // Default value + [InlineData(LineJoin.Bevel)] + [InlineData(LineJoin.Round)] + [InlineData(LineJoin.MiterClipped)] + // Invalid (non-enum) values are allowed. Their values are stored and returned unchanged. + [InlineData(LineJoin.Miter - 1)] + [InlineData(LineJoin.MiterClipped + 1)] + public void StrokeJoin_SetThenGet_Success(LineJoin lineJoin) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.StrokeJoin = lineJoin; + Assert.Equal(lineJoin, customLineCap.StrokeJoin); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineCap.Flat)] // Default value + [InlineData(LineCap.Square)] + [InlineData(LineCap.Round)] + [InlineData(LineCap.Triangle)] + public void BaseCap_SetThenGet_Success(LineCap baseCap) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.BaseCap = baseCap; + Assert.Equal(baseCap, customLineCap.BaseCap); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(LineCap.NoAnchor)] + [InlineData(LineCap.SquareAnchor)] + [InlineData(LineCap.RoundAnchor)] + [InlineData(LineCap.DiamondAnchor)] + [InlineData(LineCap.Custom)] + [InlineData(LineCap.Flat - 1)] + [InlineData(LineCap.Custom + 1)] + public void BaseCap_Set_InvalidLineCap_ThrowsArgumentException(LineCap baseCap) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + AssertExtensions.Throws(null, () => customLineCap.BaseCap = baseCap); + Assert.Equal(LineCap.Flat, customLineCap.BaseCap); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0f)] + [InlineData(1f)] + [InlineData(10f)] + [InlineData(10000f)] + [InlineData(-1f)] + [InlineData(-10f)] + [InlineData(-10000f)] + [InlineData(float.MaxValue)] + [InlineData(float.MinValue)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.NaN)] + public void BaseInset_SetThenGet_Success(float value) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.BaseInset = value; + Assert.Equal(value, customLineCap.BaseInset); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0f)] + [InlineData(1f)] + [InlineData(10f)] + [InlineData(10000f)] + [InlineData(-1f)] + [InlineData(-10f)] + [InlineData(-10000f)] + [InlineData(float.MaxValue)] + [InlineData(float.MinValue)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.NaN)] + public void WidthScale_SetThenGet_Success(float value) + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.WidthScale = value; + Assert.Equal(value, customLineCap.WidthScale); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Disposed_MembersThrow() + { + using (GraphicsPath strokePath = new GraphicsPath()) + using (CustomLineCap customLineCap = new CustomLineCap(null, strokePath)) + { + customLineCap.Dispose(); + AssertExtensions.Throws(null, () => customLineCap.StrokeJoin); + AssertExtensions.Throws(null, () => customLineCap.BaseCap); + AssertExtensions.Throws(null, () => customLineCap.BaseInset); + AssertExtensions.Throws(null, () => customLineCap.WidthScale); + AssertExtensions.Throws(null, () => customLineCap.Clone()); + AssertExtensions.Throws(null, () => customLineCap.SetStrokeCaps(LineCap.Flat, LineCap.Flat)); + AssertExtensions.Throws(null, () => customLineCap.GetStrokeCaps(out LineCap startCap, out LineCap endCap)); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathIteratorTests.cs b/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathIteratorTests.cs new file mode 100644 index 00000000000..91326943d92 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathIteratorTests.cs @@ -0,0 +1,424 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class GraphicsPathIteratorTests + { + private readonly PointF[] _twoPoints = new PointF[2] { new PointF(1, 2), new PointF(20, 30) }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Path_Success() + { + byte[] types = new byte[] { 0, 1 }; + + using (GraphicsPath gp = new GraphicsPath(_twoPoints, types)) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.Count); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(0, gpi.Count); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullPath_Success() + { + using (GraphicsPathIterator gpi = new GraphicsPathIterator(null)) + { + Assert.Equal(0, gpi.Count); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextSubpath_PathFigureNotClosed_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + gp.AddLines(_twoPoints); + Assert.Equal(0, gpi.NextSubpath(gp, out bool isClosed)); + Assert.False(isClosed); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextSubpath_PathFigureClosed_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath(_twoPoints, new byte[] { 0, 129 })) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.NextSubpath(gp, out bool isClosed)); + Assert.True(isClosed); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextSubpath_NullPath_ReturnsExpected() + { + using (GraphicsPathIterator gpi = new GraphicsPathIterator(null)) + { + Assert.Equal(0, gpi.NextSubpath(null, out bool isClosed)); + Assert.False(isClosed); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextSubpath_FigureNotClosed_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + gp.AddLines(_twoPoints); + Assert.Equal(0, gpi.NextSubpath(out int startIndex, out int endIndex, out bool isClosed)); + Assert.False(isClosed); + Assert.Equal(0, startIndex); + Assert.Equal(0, endIndex); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextSubpath_FigureClosed_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath(_twoPoints, new byte[] { 0, 129 })) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.NextSubpath(out int startIndex, out int endIndex, out bool isClosed)); + Assert.True(isClosed); + Assert.Equal(0, startIndex); + Assert.Equal(1, endIndex); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextMarker_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath(_twoPoints, new byte[] { 0, 1 })) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.NextMarker(out int startIndex, out int endIndex)); + Assert.Equal(0, startIndex); + Assert.Equal(1, endIndex); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextMarker_Empty_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + gp.AddLines(_twoPoints); + Assert.Equal(0, gpi.NextMarker(out int startIndex, out int endIndex)); + Assert.Equal(0, startIndex); + Assert.Equal(0, endIndex); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextMarker_NullPath_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + gp.AddLines(_twoPoints); + Assert.Equal(0, gpi.NextMarker(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextMarker_EmptyPath_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + gp.AddLines(_twoPoints); + Assert.Equal(0, gpi.NextMarker(gp)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NextMarker_Path_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath(_twoPoints, new byte[] { 0, 1 })) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.NextMarker(gp)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Count_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath(_twoPoints, new byte[] { 0, 1 })) + using (GraphicsPath gpEmpty = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + using (GraphicsPathIterator gpiEmpty = new GraphicsPathIterator(gpEmpty)) + using (GraphicsPathIterator gpiNull = new GraphicsPathIterator(null)) + { + Assert.Equal(2, gpi.Count); + Assert.Equal(0, gpiEmpty.Count); + Assert.Equal(0, gpiNull.Count); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SubpathCount_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + using (GraphicsPathIterator gpiNull = new GraphicsPathIterator(null)) + { + Assert.Equal(0, gpi.SubpathCount); + Assert.Equal(0, gpiNull.SubpathCount); + + gp.AddLine(0, 1, 2, 3); + gp.SetMarkers(); + gp.StartFigure(); + gp.AddLine(20, 21, 22, 23); + gp.AddBezier(5, 6, 7, 8, 9, 10, 11, 12); + + using (GraphicsPathIterator gpiWithSubpaths = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpiWithSubpaths.SubpathCount); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void HasCurve_ReturnsExpected() + { + Point[] points = new Point[] { new Point(1, 1), new Point(2, 2), new Point(3, 3), new Point(4, 4) }; + byte[] types = new byte[] { 0, 3, 3, 3 }; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPath gpEmpty = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + using (GraphicsPathIterator gpiEmpty = new GraphicsPathIterator(gpEmpty)) + using (GraphicsPathIterator gpiNull = new GraphicsPathIterator(null)) + { + Assert.True(gpi.HasCurve()); + Assert.False(gpiEmpty.HasCurve()); + Assert.False(gpiNull.HasCurve()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Rewind_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath inner = new GraphicsPath()) + { + gp.AddLine(0, 1, 2, 3); + gp.SetMarkers(); + gp.StartFigure(); + gp.AddLine(20, 21, 22, 23); + gp.AddBezier(5, 6, 7, 8, 9, 10, 11, 12); + + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(2, gpi.SubpathCount); + Assert.Equal(2, gpi.NextMarker(gp)); + Assert.Equal(6, gpi.NextMarker(gp)); + Assert.Equal(0, gpi.NextMarker(gp)); + gpi.Rewind(); + Assert.Equal(8, gpi.NextMarker(gp)); + Assert.Equal(0, gpi.NextMarker(gp)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Enumerate_ZeroPoints_ReturnsExpected() + { + PointF[] points = new PointF[0]; + byte[] types = new byte[0]; + + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(0, gpi.Enumerate(ref points, ref types)); + Assert.Equal(0, points.Length); + Assert.Equal(0, types.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Enumerate_ReturnsExpected() + { + PointF[] points = new PointF[] { new PointF(1f, 1f), new PointF(2f, 2f), new PointF(3f, 3f), new PointF(4f, 4f) }; + byte[] types = new byte[] { 0, 3, 3, 3 }; + + PointF[] actualPoints = new PointF[4]; + byte[] actualTypes = new byte[4]; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(4, gpi.Enumerate(ref actualPoints, ref actualTypes)); + Assert.Equal(gp.PathPoints, actualPoints); + Assert.Equal(gp.PathTypes, actualTypes); + } + } + + public static IEnumerable PointsTypesLengthMismatch_TestData() + { + yield return new object[] { new PointF[1], new byte[2] }; + yield return new object[] { new PointF[2], new byte[1] }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(PointsTypesLengthMismatch_TestData))] + public void Enumerate_PointsTypesMismatch_ThrowsArgumentException(PointF[] points, byte[] types) + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + AssertExtensions.Throws(null, () => gpi.Enumerate(ref points, ref types)); + } + } + + public static IEnumerable NullPointsTypes_TestData() + { + yield return new object[] { null, new byte[1] }; + yield return new object[] { new PointF[1], null }; + yield return new object[] { null, null }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(NullPointsTypes_TestData))] + public void Enumerate_NullPointsTypes_ThrowsNullReferenceException(PointF[] points, byte[] types) + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Throws(() => gpi.Enumerate(ref points, ref types)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(PointsTypesLengthMismatch_TestData))] + public void CopyData_PointsTypesMismatch_ThrowsArgumentException(PointF[] points, byte[] types) + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + AssertExtensions.Throws(null, () => gpi.CopyData(ref points, ref types, 0, points.Length)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(NullPointsTypes_TestData))] + public void CopyData_NullPointsTypes_ThrowsNullReferenceException(PointF[] points, byte[] types) + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Throws(() => gpi.CopyData(ref points, ref types, 0, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, 2)] + [InlineData(0, 3)] + public void CopyData_StartEndIndexesOutOfRange_ThrowsArgumentException(int startIndex, int endIndex) + { + PointF[] resultPoints = new PointF[0]; + byte[] resultTypes = new byte[0]; + + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + AssertExtensions.Throws(null, () => gpi.CopyData(ref resultPoints, ref resultTypes, startIndex, endIndex)); + } + } + + public static IEnumerable CopyData_StartEndIndexesOutOfRange_TestData() + { + yield return new object[] { new PointF[3], new byte[3], int.MinValue, 2 }; + yield return new object[] { new PointF[3], new byte[3], 0, int.MaxValue }; + yield return new object[] { new PointF[3], new byte[3], 2, 0 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CopyData_StartEndIndexesOutOfRange_TestData))] + public void CopyData_StartEndIndexesOutOfRange_ReturnsExpected(PointF[] points, byte[] types, int startIndex, int endIndex) + { + PointF[] resultPoints = new PointF[points.Length]; + byte[] resultTypes = new byte[points.Length]; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(0, gpi.CopyData(ref resultPoints, ref resultTypes, startIndex, endIndex)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CopyData_EqualStartEndIndexes_ReturnsExpected() + { + PointF[] points = new PointF[] { new PointF(1f, 1f), new PointF(2f, 2f), new PointF(3f, 3f), new PointF(4f, 4f) }; + byte[] types = new byte[] { 0, 3, 3, 3 }; + + PointF[] actualPoints = new PointF[1]; + byte[] actualTypes = new byte[1]; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(1, gpi.CopyData(ref actualPoints, ref actualTypes, 0, 0)); + Assert.Equal(gp.PathPoints[0], actualPoints[0]); + Assert.Equal(gp.PathTypes[0], actualTypes[0]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CopyData_ReturnsExpected() + { + PointF[] points = new PointF[] { new PointF(1f, 1f), new PointF(2f, 2f), new PointF(3f, 3f), new PointF(4f, 4f) }; + byte[] types = new byte[] { 0, 3, 3, 3 }; + + PointF[] actualPoints = new PointF[3]; + byte[] actualTypes = new byte[3]; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPathIterator gpi = new GraphicsPathIterator(gp)) + { + Assert.Equal(3, gpi.CopyData(ref actualPoints, ref actualTypes, 0, 2)); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathTests.cs b/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathTests.cs new file mode 100644 index 00000000000..74705e42f5a --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/GraphicsPathTests.cs @@ -0,0 +1,2714 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.ComponentModel; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class GraphicsPathTests + { + private const float Pi4 = (float)(Math.PI / 4); + private const float Delta = 0.0003f; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Equal(FillMode.Alternate, gp.FillMode); + AssertEmptyGraphicsPath(gp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_FillMode_Success() + { + using (GraphicsPath gpa = new GraphicsPath(FillMode.Alternate)) + using (GraphicsPath gpw = new GraphicsPath(FillMode.Winding)) + { + Assert.Equal(FillMode.Alternate, gpa.FillMode); + AssertEmptyGraphicsPath(gpa); + Assert.Equal(FillMode.Winding, gpw.FillMode); + AssertEmptyGraphicsPath(gpw); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_SamePoints_Success() + { + byte[] types = new byte[6] { 0, 1, 1, 1, 1, 1 }; + Point[] points = new Point[] + { + new Point (1, 1), new Point (1, 1), new Point (1, 1), + new Point (1, 1), new Point (1, 1), new Point (1, 1), + }; + + PointF[] fPoints = new PointF[] + { + new PointF (1f, 1f), new PointF (1f, 1f), new PointF (1f, 1f), + new PointF (1f, 1f), new PointF (1f, 1f), new PointF (1f, 1f), + }; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + using (GraphicsPath gpf = new GraphicsPath(fPoints, types)) + { + Assert.Equal(FillMode.Alternate, gp.FillMode); + Assert.Equal(6, gp.PointCount); + Assert.Equal(FillMode.Alternate, gpf.FillMode); + Assert.Equal(6, gpf.PointCount); + types[0] = 1; + Assert.Equal(FillMode.Alternate, gp.FillMode); + Assert.Equal(6, gp.PointCount); + Assert.Equal(FillMode.Alternate, gpf.FillMode); + Assert.Equal(6, gpf.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PointsNull_ThrowsArgumentNullException() + { + AssertExtensions.Throws("pts", () => new GraphicsPath((Point[])null, new byte[1])); + } + + public static IEnumerable AddCurve_PointsTypesLengthMismatch_TestData() + { + yield return new object[] { 1, 2 }; + yield return new object[] { 2, 1 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddCurve_PointsTypesLengthMismatch_TestData))] + public void Ctor_PointsTypesLengthMismatch_ThrowsArgumentException(int pointsLength, int typesLength) + { + AssertExtensions.Throws(null, () => new GraphicsPath(new Point[pointsLength], new byte[typesLength])); + AssertExtensions.Throws(null, () => new GraphicsPath(new PointF[pointsLength], new byte[typesLength])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + Assert.Equal(FillMode.Alternate, clone.FillMode); + AssertEmptyGraphicsPath(clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reset_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.Reset(); + + Assert.Equal(FillMode.Alternate, gp.FillMode); + AssertEmptyGraphicsPath(gp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GraphicsPath_FillModeChange() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.FillMode = FillMode.Winding; + Assert.Equal(FillMode.Winding, gp.FillMode); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(FillMode.Alternate - 1)] + [InlineData(FillMode.Winding + 1)] + public void GraphicsPath_InvalidFillMode_ThrowsInvalidEnumArgumentException(FillMode fillMode) + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.ThrowsAny(() => gp.FillMode = fillMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathData_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Equal(0, gp.PathData.Points.Length); + Assert.Equal(0, gp.PathData.Types.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathData_CannotChange() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(1, 1, 2, 2)); + Assert.Equal(1f, gp.PathData.Points[0].X); + Assert.Equal(1f, gp.PathData.Points[0].Y); + + gp.PathData.Points[0] = new Point(0, 0); + Assert.Equal(1f, gp.PathData.Points[0].X); + Assert.Equal(1f, gp.PathData.Points[0].Y); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathPoints_CannotChange() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(1, 1, 2, 2)); + Assert.Equal(1f, gp.PathPoints[0].X); + Assert.Equal(1f, gp.PathPoints[0].Y); + + gp.PathPoints[0] = new Point(0, 0); + Assert.Equal(1f, gp.PathPoints[0].X); + Assert.Equal(1f, gp.PathPoints[0].Y); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathPoints_EmptyPath_ThrowsArgumentException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Throws(() => gp.PathPoints); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathTypes_CannotChange() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(1, 1, 2, 2)); + Assert.Equal(0, gp.PathTypes[0]); + + gp.PathTypes[0] = 1; + Assert.Equal(0, gp.PathTypes[0]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PathTypes_EmptyPath_ThrowsArgumentException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Throws(() => gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetLastPoint_ReturnsExpected() + { + byte[] types = new byte[3] { 0, 1, 1 }; + PointF[] points = new PointF[] + { + new PointF (1f, 1f), new PointF (2f, 2f), new PointF (3f, 3f), + }; + + using (GraphicsPath gp = new GraphicsPath(points, types)) + { + Assert.Equal(gp.GetLastPoint(), points[2]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLine_Success() + { + using (GraphicsPath gpInt = new GraphicsPath()) + using (GraphicsPath gpFloat = new GraphicsPath()) + using (GraphicsPath gpPointsInt = new GraphicsPath()) + using (GraphicsPath gpfPointsloat = new GraphicsPath()) + { + gpInt.AddLine(1, 1, 2, 2); + // AssertLine() method expects line drawn between points with coordinates 1, 1 and 2, 2, here and below. + AssertLine(gpInt); + + gpFloat.AddLine(1, 1, 2, 2); + AssertLine(gpFloat); + + gpPointsInt.AddLine(new Point(1, 1), new Point(2, 2)); + AssertLine(gpPointsInt); + + gpfPointsloat.AddLine(new PointF(1, 1), new PointF(2, 2)); + AssertLine(gpfPointsloat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLine_SamePoints_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddLine(new Point(49, 157), new Point(75, 196)); + gpi.AddLine(new Point(75, 196), new Point(102, 209)); + Assert.Equal(3, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1 }, gpi.PathTypes); + + gpi.AddLine(new Point(102, 209), new Point(75, 196)); + Assert.Equal(4, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 1 }, gpi.PathTypes); + + gpf.AddLine(new PointF(49, 157), new PointF(75, 196)); + gpf.AddLine(new PointF(75, 196), new PointF(102, 209)); + Assert.Equal(3, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1 }, gpf.PathTypes); + + gpf.AddLine(new PointF(102, 209), new PointF(75, 196)); + Assert.Equal(4, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 1 }, gpf.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLines_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddLines(new Point[] { new Point(1, 1), new Point(2, 2) }); + AssertLine(gpi); + + gpf.AddLines(new PointF[] { new PointF(1, 1), new PointF(2, 2) }); + AssertLine(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLines_SinglePoint_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddLines(new PointF[] { new PointF(1, 1) }); + Assert.Equal(1, gpi.PointCount); + Assert.Equal(0, gpi.PathTypes[0]); + + gpf.AddLines(new PointF[] { new PointF(1, 1) }); + Assert.Equal(1, gpf.PointCount); + Assert.Equal(0, gpf.PathTypes[0]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLines_SamePoint_Success() + { + Point[] intPoints = new Point[] + { + new Point(49, 157), new Point(49, 157) + }; + + PointF[] floatPoints = new PointF[] + { + new PointF(49, 57), new PointF(49, 57), + new PointF(49, 57), new PointF(49, 57) + }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddLines(intPoints); + Assert.Equal(2, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1 }, gpi.PathTypes); + + gpi.AddLines(intPoints); + Assert.Equal(3, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1 }, gpi.PathTypes); + + gpi.AddLines(intPoints); + Assert.Equal(4, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 1 }, gpi.PathTypes); + + gpf.AddLines(floatPoints); + Assert.Equal(4, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 1 }, gpf.PathTypes); + + gpf.AddLines(floatPoints); + Assert.Equal(7, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 1, 1, 1, 1 }, gpf.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLines_PointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("points", () => new GraphicsPath().AddLines((Point[])null)); + AssertExtensions.Throws("points", () => new GraphicsPath().AddLines((PointF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddLines_ZeroPoints_ThrowsArgumentException() + { + AssertExtensions.Throws("points", null, () => new GraphicsPath().AddLines(new Point[0])); + AssertExtensions.Throws("points", null, () => new GraphicsPath().AddLines(new PointF[0])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddArc_Values_Success() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddArc(1, 1, 2, 2, Pi4, Pi4); + // AssertArc() method expects added Arc with parameters + // x=1, y=1, width=2, height=2, startAngle=Pi4, seewpAngle=Pi4 here and below. + AssertArc(gpi); + + gpf.AddArc(1f, 1f, 2f, 2f, Pi4, Pi4); + AssertArc(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddArc_Rectangle_Success() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddArc(new Rectangle(1, 1, 2, 2), Pi4, Pi4); + AssertArc(gpi); + + gpf.AddArc(new RectangleF(1, 1, 2, 2), Pi4, Pi4); + AssertArc(gpf); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(0, 0)] + [InlineData(1, 0)] + [InlineData(0, 1)] + public void AddArc_ZeroWidthHeight_ThrowsArgumentException(int width, int height) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddArc(1, 1, width, height, Pi4, Pi4)); + AssertExtensions.Throws(null, () => gp.AddArc(1.0f, 1.0f, (float)width, (float)height, Pi4, Pi4)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddBezier_Points_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddBezier(new Point(1, 1), new Point(2, 2), new Point(3, 3), new Point(4, 4)); + // AssertBezier() method expects added Bezier with points (1, 1), (2, 2), (3, 3), (4, 4), here and below. + AssertBezier(gpi); + + gpf.AddBezier(new PointF(1, 1), new PointF(2, 2), new PointF(3, 3), new PointF(4, 4)); + AssertBezier(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddBezier_SamePoints_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gp.AddBezier(new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 0)); + Assert.Equal(4, gp.PointCount); + Assert.Equal(new byte[] { 0, 3, 3, 3 }, gp.PathTypes); + + gp.AddBezier(new Point(0, 0), new Point(0, 0), new Point(0, 0), new Point(0, 0)); + Assert.Equal(7, gp.PointCount); + Assert.Equal(new byte[] { 0, 3, 3, 3, 3, 3, 3 }, gp.PathTypes); + + gpf.AddBezier(new PointF(0, 0), new PointF(0, 0), new PointF(0, 0), new PointF(0, 0)); + Assert.Equal(4, gpf.PointCount); + Assert.Equal(new byte[] { 0, 3, 3, 3 }, gpf.PathTypes); + + gpf.AddBezier(new PointF(0, 0), new PointF(0, 0), new PointF(0, 0), new PointF(0, 0)); + Assert.Equal(7, gpf.PointCount); + Assert.Equal(new byte[] { 0, 3, 3, 3, 3, 3, 3 }, gpf.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddBezier_Values_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddBezier(1, 1, 2, 2, 3, 3, 4, 4); + AssertBezier(gpi); + + gpf.AddBezier(1f, 1f, 2f, 2f, 3f, 3f, 4f, 4f); + AssertBezier(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddBeziers_Points_Success() + { + PointF[] points = new PointF[] + { + new PointF(1, 1), new PointF(2, 2), new PointF(3, 3), new PointF(4, 4) + }; + + using (GraphicsPath gpf = new GraphicsPath()) + { + gpf.AddBeziers(points); + AssertBezier(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddBeziers_PointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("points", () => gp.AddBeziers((PointF[])null)); + AssertExtensions.Throws("points", () => gp.AddBeziers((Point[])null)); + } + } + + public static IEnumerable AddBeziers_InvalidFloatPointsLength_TestData() + { + yield return new object[] { new PointF[0] }; + yield return new object[] { new PointF[1] { new PointF(1f, 1f) } }; + yield return new object[] { new PointF[2] { new PointF(1f, 1f), new PointF(2f, 2f) } }; + yield return new object[] { new PointF[3] { new PointF(1f, 1f), new PointF(2f, 2f), new PointF(3f, 3f) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddBeziers_InvalidFloatPointsLength_TestData))] + public void AddBeziers_InvalidFloatPointsLength_ThrowsArgumentException(PointF[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddBeziers(points)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_TwoPoints_Success() + { + Point[] intPoints = new Point[] { new Point(1, 1), new Point(2, 2) }; + PointF[] floatPoints = new PointF[] { new PointF(1, 1), new PointF(2, 2) }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpf.AddCurve(floatPoints); + // AssertCurve() method expects added Curve with points (1, 1), (2, 2), here and below. + AssertCurve(gpf); + + gpi.AddCurve(intPoints); + AssertCurve(gpi); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_TwoPointsWithTension_Success() + { + Point[] intPoints = new Point[] { new Point(1, 1), new Point(2, 2) }; + PointF[] floatPoints = new PointF[] { new PointF(1, 1), new PointF(2, 2) }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddCurve(intPoints, 0.5f); + AssertCurve(gpi); + + gpf.AddCurve(floatPoints, 0.5f); + AssertCurve(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_SamePoints_Success() + { + Point[] intPoints = new Point[] { new Point(1, 1), new Point(1, 1) }; + PointF[] floatPoints = new PointF[] { new PointF(1, 1), new PointF(1, 1) }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddCurve(intPoints); + Assert.Equal(4, gpi.PointCount); + gpi.AddCurve(intPoints); + Assert.Equal(7, gpi.PointCount); + + gpf.AddCurve(floatPoints); + Assert.Equal(4, gpf.PointCount); + gpf.AddCurve(floatPoints); + Assert.Equal(7, gpf.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_LargeTension_Success() + { + Point[] intPoints = new Point[] { new Point(1, 1), new Point(2, 2) }; + PointF[] floatPoints = new PointF[] { new PointF(1, 1), new PointF(2, 2) }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddCurve(intPoints, float.MaxValue); + Assert.Equal(4, gpi.PointCount); + + gpf.AddCurve(floatPoints, float.MaxValue); + Assert.Equal(4, gpf.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_Success() + { + PointF[] points = new PointF[] + { + new PointF (37f, 185f), + new PointF (99f, 185f), + new PointF (161f, 159f), + new PointF (223f, 185f), + new PointF (285f, 54f), + }; + + PointF[] expectedPoints = new PointF[] + { + new PointF (37f, 185f), + new PointF (47.33333f, 185f), + new PointF (78.3333f, 189.3333f), + new PointF (99f, 185f), + new PointF (119.6667f, 180.6667f), + new PointF (140.3333f, 159f), + new PointF (161f, 159f), + new PointF (181.6667f, 159f), + new PointF (202.3333f, 202.5f), + new PointF (223f, 185f), + new PointF (243.6667f, 167.5f), + new PointF (274.6667f, 75.8333f), + new PointF (285f, 54f), + }; + + byte[] expectedTypes = new byte[] { 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; + int[] pointsCount = { 4, 7, 10, 13 }; + using (GraphicsPath gp = new GraphicsPath()) + { + for (int i = 0; i < points.Length - 1; i++) + { + gp.AddCurve(points, i, 1, 0.5f); + Assert.Equal(pointsCount[i], gp.PointCount); + } + + AssertPointsSequenceEqual(expectedPoints, gp.PathPoints, Delta); + Assert.Equal(expectedTypes, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_PointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("points", () => gp.AddCurve((PointF[])null)); + AssertExtensions.Throws("points", () => gp.AddCurve((Point[])null)); + } + } + + public static IEnumerable AddCurve_InvalidFloatPointsLength_TestData() + { + yield return new object[] { new PointF[0] }; + yield return new object[] { new PointF[1] { new PointF(1f, 1f) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddCurve_InvalidFloatPointsLength_TestData))] + public void AddCurve_InvalidFloatPointsLength_ThrowsArgumentException(PointF[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddCurve(points)); + AssertExtensions.Throws(null, () => gp.AddCurve(points, 0, 2, 0.5f)); + } + } + + public static IEnumerable AddCurve_InvalidPointsLength_TestData() + { + yield return new object[] { new Point[0] }; + yield return new object[] { new Point[1] { new Point(1, 1) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddCurve_InvalidPointsLength_TestData))] + public void AddCurve_InvalidPointsLength_ThrowsArgumentException(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddCurve(points)); + AssertExtensions.Throws(null, () => gp.AddCurve(points, 0, 2, 0.5f)); + } + } + + public static IEnumerable AddCurve_InvalidSegment_TestData() + { + yield return new object[] { 0 }; + yield return new object[] { -1 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddCurve_InvalidSegment_TestData))] + public void AddCurve_InvalidSegment_ThrowsArgumentException(int segment) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddCurve( + new PointF[2] { new PointF(1f, 1f), new PointF(2f, 2f) }, 0, segment, 0.5f)); + + AssertExtensions.Throws(null, () => gp.AddCurve( + new Point[2] { new Point(1, 1), new Point(2, 2) }, 0, segment, 0.5f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddCurve_OffsetTooLarge_ThrowsArgumentException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddCurve( + new PointF[3] { new PointF(1f, 1f), new PointF(0f, 20f), new PointF(20f, 0f) }, 1, 2, 0.5f)); + + AssertExtensions.Throws(null, () => gp.AddCurve( + new Point[3] { new Point(1, 1), new Point(0, 20), new Point(20, 0) }, 1, 2, 0.5f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddClosedCurve_Points_Success() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddClosedCurve(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + // AssertClosedCurve() method expects added ClosedCurve with points (1, 1), (2, 2), (3, 3), here and below. + AssertClosedCurve(gpi); + + gpf.AddClosedCurve(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + AssertClosedCurve(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddClosedCurve_SamePoints_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddClosedCurve(new Point[3] { new Point(1, 1), new Point(1, 1), new Point(1, 1) }); + Assert.Equal(10, gpi.PointCount); + gpi.AddClosedCurve(new Point[3] { new Point(1, 1), new Point(1, 1), new Point(1, 1) }); + Assert.Equal(20, gpi.PointCount); + + gpf.AddClosedCurve(new PointF[3] { new PointF(1, 1), new PointF(1, 1), new PointF(1, 1) }); + Assert.Equal(10, gpf.PointCount); + gpf.AddClosedCurve(new PointF[3] { new PointF(1, 1), new PointF(1, 1), new PointF(1, 1) }); + Assert.Equal(20, gpf.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddClosedCurve_Tension_Success() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddClosedCurve(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }, 0.5f); + AssertClosedCurve(gpi); + + gpf.AddClosedCurve(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }, 0.5f); + AssertClosedCurve(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddClosedCurve_PointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("points", () => gp.AddClosedCurve((PointF[])null)); + AssertExtensions.Throws("points", () => gp.AddClosedCurve((Point[])null)); + } + } + + public static IEnumerable AddClosedCurve_InvalidPointsLength_TestData() + { + yield return new object[] { new Point[0] }; + yield return new object[] { new Point[1] { new Point(1, 1) } }; + yield return new object[] { new Point[2] { new Point(1, 1), new Point(2, 2) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddCurve_InvalidPointsLength_TestData))] + public void AddClosedCurve_InvalidPointsLength_ThrowsArgumentException(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddClosedCurve(points)); + } + } + + public static IEnumerable AddClosedCurve_InvalidFloatPointsLength_TestData() + { + yield return new object[] { new PointF[0] }; + yield return new object[] { new PointF[1] { new PointF(1f, 1f) } }; + yield return new object[] { new PointF[2] { new PointF(1f, 1f), new PointF(2f, 2f) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddClosedCurve_InvalidFloatPointsLength_TestData))] + public void AddClosedCurve_InvalidFloatPointsLength_ThrowsArgumentException(PointF[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddClosedCurve(points)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddRectangle_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddRectangle(new Rectangle(1, 1, 2, 2)); + // AssertRectangle() method expects added Rectangle with parameters x=1, y=1, width=2, height=2, here and below. + AssertRectangle(gpi); + + gpf.AddRectangle(new RectangleF(1, 1, 2, 2)); + AssertRectangle(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddRectangle_SameRectangles_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddRectangle(new Rectangle(1, 1, 1, 1)); + Assert.Equal(4, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 129 }, gpi.PathTypes); + + PointF endI = gpi.PathPoints[3]; + + gpi.AddRectangle(new Rectangle((int)endI.X, (int)endI.Y, 1, 1)); + Assert.Equal(8, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 129, 0, 1, 1, 129 }, gpi.PathTypes); + + gpf.AddRectangle(new RectangleF(1, 1, 1, 1)); + Assert.Equal(4, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 129 }, gpf.PathTypes); + Assert.Equal(129, gpf.PathTypes[3]); + + PointF endF = gpf.PathPoints[3]; + + gpf.AddRectangle(new RectangleF(endF.X, endF.Y, 1, 1)); + Assert.Equal(8, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 1, 129, 0, 1, 1, 129 }, gpf.PathTypes); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(3, 0)] + [InlineData(0, 4)] + public void AddRectangle_ZeroWidthHeight_Success(int width, int height) + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddRectangle(new Rectangle(1, 2, width, height)); + Assert.Equal(0, gpi.PathData.Points.Length); + + gpf.AddRectangle(new RectangleF(1f, 2f, (float)width, (float)height)); + Assert.Equal(0, gpf.PathData.Points.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddRectangles_Success() + { + Rectangle[] rectInt = new Rectangle[] { new Rectangle(1, 1, 2, 2), new Rectangle(3, 3, 4, 4) }; + RectangleF[] rectFloat = new RectangleF[] { new RectangleF(1, 1, 2, 2), new RectangleF(3, 3, 4, 4) }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddRectangles(rectInt); + Assert.Equal(8, gpi.PathPoints.Length); + Assert.Equal(8, gpi.PathTypes.Length); + Assert.Equal(8, gpi.PathData.Points.Length); + + gpf.AddRectangles(rectFloat); + Assert.Equal(8, gpf.PathPoints.Length); + Assert.Equal(8, gpf.PathTypes.Length); + Assert.Equal(8, gpf.PathData.Points.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddRectangles_SamePoints_Success() + { + Rectangle[] rectInt = new Rectangle[] + { + new Rectangle(1, 1, 0, 0), + new Rectangle(1, 1, 2, 2), + new Rectangle(1, 1, 2, 2) + }; + + RectangleF[] rectFloat = new RectangleF[] + { + new RectangleF(1, 1, 0f, 0f), + new RectangleF(1, 1, 2, 2), + new RectangleF(1, 1, 2, 2) + }; + + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddRectangles(rectInt); + Assert.Equal(8, gpi.PathPoints.Length); + Assert.Equal(8, gpi.PathTypes.Length); + Assert.Equal(8, gpi.PathData.Points.Length); + + gpf.AddRectangles(rectFloat); + Assert.Equal(8, gpf.PathPoints.Length); + Assert.Equal(8, gpf.PathTypes.Length); + Assert.Equal(8, gpf.PathData.Points.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddRectangles_RectangleNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("rects", () => gp.AddRectangles((RectangleF[])null)); + AssertExtensions.Throws("rects", () => gp.AddRectangles((Rectangle[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddEllipse_Rectangle_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddEllipse(new Rectangle(1, 1, 2, 2)); + // AssertEllipse() method expects added Ellipse with parameters x=1, y=1, width=2, height=2, here and below. + AssertEllipse(gpi); + + gpf.AddEllipse(new RectangleF(1, 1, 2, 2)); + AssertEllipse(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddEllipse_Values_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddEllipse(1, 1, 2, 2); + AssertEllipse(gpi); + + gpf.AddEllipse(1f, 1f, 2f, 2f); + AssertEllipse(gpf); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(2, 0)] + [InlineData(0, 2)] + public void AddEllipse_ZeroWidthHeight_Success(int width, int height) + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddEllipse(1, 1, width, height); + Assert.Equal(13, gpi.PathData.Points.Length); + + gpf.AddEllipse(1f, 2f, (float)width, (float)height); + Assert.Equal(13, gpf.PathData.Points.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPie_Rectangle_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + { + gpi.AddPie(new Rectangle(1, 1, 2, 2), Pi4, Pi4); + // AssertPie() method expects added Pie with parameters + // x=1, y=1, width=2, height=2, startAngle=Pi4, seewpAngle=Pi4 here and below. + AssertPie(gpi); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPie_Values_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddPie(1, 1, 2, 2, Pi4, Pi4); + AssertPie(gpi); + + gpf.AddPie(1f, 1f, 2f, 2f, Pi4, Pi4); + AssertPie(gpf); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(0, 0)] + [InlineData(2, 0)] + [InlineData(0, 2)] + public void AddPie_ZeroWidthHeight_ThrowsArgumentException(int width, int height) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddPie(1, 1, height, width, Pi4, Pi4)); + AssertExtensions.Throws(null, () => gp.AddPie(1f, 1f, height, width, Pi4, Pi4)); + AssertExtensions.Throws(null, () => gp.AddPie(new Rectangle(1, 1, height, width), Pi4, Pi4)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPolygon_Points_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + // AssertPolygon() method expects added Polygon with points (1, 1), (2, 2), (3, 3), here and below. + AssertPolygon(gpi); + + gpf.AddPolygon(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + AssertPolygon(gpf); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPolygon_SamePoints_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + Assert.Equal(3, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 129 }, gpi.PathTypes); + + gpi.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + Assert.Equal(6, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129 }, gpi.PathTypes); + + gpi.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + Assert.Equal(9, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129, 0, 1, 129 }, gpi.PathTypes); + + gpi.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + Assert.Equal(12, gpi.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129, 0, 1, 129, 0, 1, 129 }, gpi.PathTypes); + + gpf.AddPolygon(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + Assert.Equal(3, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 129 }, gpf.PathTypes); + + gpf.AddPolygon(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + Assert.Equal(6, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129 }, gpf.PathTypes); + + gpf.AddPolygon(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + Assert.Equal(9, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129, 0, 1, 129 }, gpf.PathTypes); + + gpf.AddPolygon(new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }); + Assert.Equal(12, gpf.PointCount); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 129, 0, 1, 129, 0, 1, 129 }, gpf.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPolygon_PointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("points", () => new GraphicsPath().AddPolygon((Point[])null)); + AssertExtensions.Throws("points", () => new GraphicsPath().AddPolygon((PointF[])null)); + } + } + + public static IEnumerable AddPolygon_InvalidFloadPointsLength_TestData() + { + yield return new object[] { new PointF[0] }; + yield return new object[] { new PointF[1] { new PointF(1f, 1f) } }; + yield return new object[] { new PointF[2] { new PointF(1f, 1f), new PointF(2f, 2f) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddPolygon_InvalidFloadPointsLength_TestData))] + public void AddPolygon_InvalidFloadPointsLength_ThrowsArgumentException(PointF[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddPolygon(points)); + } + } + + public static IEnumerable AddPolygon_InvalidPointsLength_TestData() + { + yield return new object[] { new Point[0] }; + yield return new object[] { new Point[1] { new Point(1, 1) } }; + yield return new object[] { new Point[2] { new Point(1, 1), new Point(2, 2) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(AddPolygon_InvalidPointsLength_TestData))] + public void AddPolygon_InvalidPointsLength_ThrowsArgumentException(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => gp.AddPolygon(points)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPath_Success() + { + using (GraphicsPath inner = new GraphicsPath()) + using (GraphicsPath gp = new GraphicsPath()) + { + inner.AddRectangle(new Rectangle(1, 1, 2, 2)); + gp.AddPath(inner, true); + AssertRectangle(gp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddPath_PathNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("addingPath", () => new GraphicsPath().AddPath(null, false)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void AddString_Point_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddString("mono", FontFamily.GenericMonospace, 0, 10, new Point(10, 10), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpi.PointCount, 0); + + gpf.AddString("mono", FontFamily.GenericMonospace, 0, 10, new PointF(10f, 10f), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpf.PointCount, 0); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void AddString_Rectangle_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddString("mono", FontFamily.GenericMonospace, 0, 10, new Rectangle(10, 10, 10, 10), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpi.PointCount, 0); + + gpf.AddString("mono", FontFamily.GenericMonospace, 0, 10, new RectangleF(10f, 10f, 10f, 10f), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpf.PointCount, 0); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void AddString_NegativeSize_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddString("mono", FontFamily.GenericMonospace, 0, -10, new Point(10, 10), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpi.PointCount, 0); + + int gpiLengthOld = gpi.PathPoints.Length; + gpi.AddString("mono", FontFamily.GenericMonospace, 0, -10, new Rectangle(10, 10, 10, 10), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpi.PointCount, gpiLengthOld); + + gpf.AddString("mono", FontFamily.GenericMonospace, 0, -10, new PointF(10f, 10f), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpf.PointCount, 0); + + int pgfLengthOld = gpf.PathPoints.Length; + gpf.AddString("mono", FontFamily.GenericMonospace, 0, -10, new RectangleF(10f, 10f, 10f, 10f), StringFormat.GenericDefault); + AssertExtensions.GreaterThan(gpf.PointCount, pgfLengthOld); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddString_StringFormat_Success() + { + using (GraphicsPath gp1 = new GraphicsPath()) + using (GraphicsPath gp2 = new GraphicsPath()) + using (GraphicsPath gp3 = new GraphicsPath()) + { + gp1.AddString("mono", FontFamily.GenericMonospace, 0, 10, new RectangleF(10f, 10f, 10f, 10f), null); + AssertExtensions.GreaterThan(gp1.PointCount, 0); + + gp2.AddString("mono", FontFamily.GenericMonospace, 0, 10, new RectangleF(10f, 10f, 10f, 10f), StringFormat.GenericDefault); + Assert.Equal(gp1.PointCount, gp2.PointCount); + + gp3.AddString("mono", FontFamily.GenericMonospace, 0, 10, new RectangleF(10f, 10f, 10f, 10f), StringFormat.GenericTypographic); + Assert.NotEqual(gp1.PointCount, gp3.PointCount); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void AddString_EmptyString_Success() + { + using (GraphicsPath gpi = new GraphicsPath()) + using (GraphicsPath gpf = new GraphicsPath()) + { + gpi.AddString(string.Empty, FontFamily.GenericMonospace, 0, 10, new Point(10, 10), StringFormat.GenericDefault); + Assert.Equal(0, gpi.PointCount); + + gpi.AddString(string.Empty, FontFamily.GenericMonospace, 0, 10, new PointF(10f, 10f), StringFormat.GenericDefault); + Assert.Equal(0, gpf.PointCount); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void AddString_StringNull_ThrowsNullReferenceException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Throws(() => + gp.AddString(null, FontFamily.GenericMonospace, 0, 10, new Point(10, 10), StringFormat.GenericDefault)); + Assert.Throws(() => + gp.AddString(null, FontFamily.GenericMonospace, 0, 10, new PointF(10f, 10f), StringFormat.GenericDefault)); + Assert.Throws(() => + gp.AddString(null, FontFamily.GenericMonospace, 0, 10, new Rectangle(10, 10, 10, 10), StringFormat.GenericDefault)); + Assert.Throws(() => + gp.AddString(null, FontFamily.GenericMonospace, 0, 10, new RectangleF(10f, 10f, 10f, 10f), StringFormat.GenericDefault)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddString_FontFamilyNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("family", null, () => + new GraphicsPath().AddString("mono", null, 0, 10, new Point(10, 10), StringFormat.GenericDefault)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix(1f, 1f, 2f, 2f, 3f, 3f)) + { + gp.AddRectangle(new Rectangle(1, 1, 2, 2)); + AssertRectangle(gp); + gp.Transform(matrix); + Assert.Equal(new float[] { 1f, 1f, 2f, 2f, 3f, 3f }, matrix.Elements); + Assert.Equal(new RectangleF(6f, 6f, 6f, 6f), gp.GetBounds()); + Assert.Equal(new PointF[] { new PointF(6f, 6f), new PointF(8f, 8f), new PointF(12f, 12f), new PointF(10f, 10f) }, gp.PathPoints); + Assert.Equal(new byte[] { 0, 1, 1, 129 }, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_PathEmpty_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix(1f, 1f, 2f, 2f, 3f, 3f)) + { + gp.Transform(matrix); + Assert.Equal(new float[] { 1f, 1f, 2f, 2f, 3f, 3f }, matrix.Elements); + AssertEmptyGraphicsPath(gp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_MatrixNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("matrix", () => gp.Transform(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_PathEmpty_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Assert.Equal(new RectangleF(0f, 0f, 0f, 0f), gp.GetBounds()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_Rectangle_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix()) + { + RectangleF rectangle = new RectangleF(1f, 1f, 2f, 2f); + gp.AddRectangle(rectangle); + Assert.Equal(rectangle, gp.GetBounds()); + Assert.Equal(rectangle, gp.GetBounds(null)); + Assert.Equal(rectangle, gp.GetBounds(matrix)); + Assert.Equal(rectangle, gp.GetBounds(null, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_Pie_ReturnsExpected() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix()) + { + Rectangle rectangle = new Rectangle(10, 10, 100, 100); + gp.AddPie(rectangle, 30, 45); + AssertRectangleEqual(new RectangleF(60f, 60f, 43.3f, 48.3f), gp.GetBounds(), 0.1f); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Empty_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.Flatten(); + Assert.Equal(gp.PointCount, clone.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_MatrixNull_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.Flatten(null); + Assert.Equal(gp.PointCount, clone.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_MatrixNullFloat_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.Flatten(null, 1f); + Assert.Equal(gp.PointCount, clone.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Arc_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddArc(0f, 0f, 100f, 100f, 30, 30); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Bezier_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddBezier(0, 0, 100, 100, 30, 30, 60, 60); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_ClosedCurve_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddClosedCurve(new Point[4] + { + new Point (0, 0), new Point (40, 20), + new Point (20, 40), new Point (40, 40) + }); + + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Curve_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddCurve(new Point[4] + { + new Point (0, 0), new Point (40, 20), + new Point (20, 40), new Point (40, 40) + }); + + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Ellipse_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddEllipse(10f, 10f, 100f, 100f); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Line_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddLine(10f, 10f, 100f, 100f); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Pie_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddPie(0, 0, 100, 100, 30, 30); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Polygon_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddPolygon(new Point[4] + { + new Point (0, 0), new Point (10, 10), + new Point (20, 20), new Point (40, 40) + }); + + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flatten_Rectangle_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath clone = Assert.IsType(gp.Clone())) + { + gp.AddRectangle(new Rectangle(0, 0, 100, 100)); + gp.Flatten(); + AssertFlats(gp, clone); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Warp_DestinationPointsNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("destPoints", () => gp.Warp(null, new RectangleF())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Warp_DestinationPointsZero_ThrowsArgumentException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws(null, () => new GraphicsPath().Warp(new PointF[0], new RectangleF())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Warp_PathEmpty_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix()) + { + Assert.Equal(0, gp.PointCount); + gp.Warp(new PointF[1] { new PointF(0, 0) }, new RectangleF(10, 20, 30, 40), matrix); + Assert.Equal(0, gp.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Warp_WarpModeInvalid_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Matrix matrix = new Matrix()) + { + gp.AddPolygon(new Point[3] { new Point(5, 5), new Point(15, 5), new Point(10, 15) }); + gp.Warp(new PointF[1] { new PointF(0, 0) }, new RectangleF(10, 20, 30, 40), matrix, (WarpMode)int.MinValue); + Assert.Equal(0, gp.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Warp_RectangleEmpty_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddPolygon(new Point[3] { new Point(5, 5), new Point(15, 5), new Point(10, 15) }); + gp.Warp(new PointF[1] { new PointF(0, 0) }, new Rectangle(), null); + AssertWrapNaN(gp); + } + } + + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetMarkers_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.SetMarkers(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetMarkers_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(new Point(1, 1), new Point(2, 2)); + Assert.Equal(1, gp.PathTypes[1]); + + gp.SetMarkers(); + Assert.Equal(33, gp.PathTypes[1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearMarkers_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(new Point(1, 1), new Point(2, 2)); + Assert.Equal(1, gp.PathTypes[1]); + + gp.SetMarkers(); + Assert.Equal(33, gp.PathTypes[1]); + + gp.ClearMarkers(); + Assert.Equal(1, gp.PathTypes[1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearMarkers_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.ClearMarkers(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CloseFigure_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(new Point(1, 1), new Point(2, 2)); + Assert.Equal(1, gp.PathTypes[1]); + + gp.CloseFigure(); + Assert.Equal(129, gp.PathTypes[1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CloseFigure_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.CloseFigure(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CloseAllFigures_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(new Point(1, 1), new Point(2, 2)); + gp.StartFigure(); + gp.AddLine(new Point(3, 3), new Point(4, 4)); + Assert.Equal(1, gp.PathTypes[1]); + Assert.Equal(1, gp.PathTypes[3]); + + gp.CloseAllFigures(); + Assert.Equal(129, gp.PathTypes[1]); + Assert.Equal(129, gp.PathTypes[3]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CloseAllFigures_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.CloseAllFigures(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddArc() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddArc(10, 10, 100, 100, 90, 180); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(3, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddBezier() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddBezier(10, 10, 100, 100, 20, 20, 200, 200); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(3, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddBeziers() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddBeziers(new Point[7] + { + new Point (10, 10), new Point (20, 10), new Point (20, 20), + new Point (30, 20), new Point (40, 40), new Point (50, 40), + new Point (50, 50) + }); + + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(3, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddClosedCurve() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddClosedCurve(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(131, types[gp.PointCount - 3]); + Assert.Equal(0, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddCurve() + { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddLine(1, 1, 2, 2); + path.AddCurve(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + path.AddLine(10, 10, 20, 20); + byte[] types = path.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(3, types[path.PointCount - 3]); + Assert.Equal(1, types[path.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddEllipse() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddEllipse(10, 10, 100, 100); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(131, types[gp.PointCount - 3]); + Assert.Equal(0, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddLine() + { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddLine(1, 1, 2, 2); + path.AddLine(5, 5, 10, 10); + path.AddLine(10, 10, 20, 20); + byte[] types = path.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(1, types[path.PointCount - 3]); + Assert.Equal(1, types[path.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddLines() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddLines(new Point[4] { new Point(10, 10), new Point(20, 10), new Point(20, 20), new Point(30, 20) }); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(1, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddPath_Connect() + { + using (GraphicsPath gp = new GraphicsPath()) + using (GraphicsPath inner = new GraphicsPath()) + { + inner.AddArc(10, 10, 100, 100, 90, 180); + gp.AddLine(1, 1, 2, 2); + gp.AddPath(inner, true); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(1, types[2]); + Assert.Equal(3, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddPath_NoConnect() + { + using (GraphicsPath inner = new GraphicsPath()) + using (GraphicsPath path = new GraphicsPath()) + { + inner.AddArc(10, 10, 100, 100, 90, 180); + path.AddLine(1, 1, 2, 2); + path.AddPath(inner, false); + path.AddLine(10, 10, 20, 20); + byte[] types = path.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(3, types[path.PointCount - 3]); + Assert.Equal(1, types[path.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddPie() + { + using (GraphicsPath path = new GraphicsPath()) + { + path.AddLine(1, 1, 2, 2); + path.AddPie(10, 10, 10, 10, 90, 180); + path.AddLine(10, 10, 20, 20); + byte[] types = path.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + + Assert.Equal(128, (types[path.PointCount - 3] & 128)); + Assert.Equal(0, types[path.PointCount - 2]); + Assert.Equal(1, types[path.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddPolygon() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddPolygon(new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(129, types[gp.PointCount - 3]); + Assert.Equal(0, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddRectangle() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddRectangle(new RectangleF(10, 10, 20, 20)); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(129, types[gp.PointCount - 3]); + Assert.Equal(0, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddRectangles() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddRectangles(new RectangleF[2] + { + new RectangleF (10, 10, 20, 20), + new RectangleF (20, 20, 10, 10) + }); + + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(129, types[gp.PointCount - 3]); + Assert.Equal(0, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartClose_AddString() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 1, 2, 2); + gp.AddString("mono", FontFamily.GenericMonospace, 0, 10, new Point(20, 20), StringFormat.GenericDefault); + gp.AddLine(10, 10, 20, 20); + byte[] types = gp.PathTypes; + + Assert.Equal(0, types[0]); + Assert.Equal(0, types[2]); + Assert.Equal(163, types[gp.PointCount - 3]); + Assert.Equal(1, types[gp.PointCount - 2]); + Assert.Equal(1, types[gp.PointCount - 1]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Widen_Pen_Success() + { + PointF[] expectedPoints = new PointF[] + { + new PointF(0.5f, 0.5f), new PointF(3.5f, 0.5f), new PointF(3.5f, 3.5f), + new PointF(0.5f, 3.5f), new PointF(1.5f, 3.0f), new PointF(1.0f, 2.5f), + new PointF(3.0f, 2.5f), new PointF(2.5f, 3.0f), new PointF(2.5f, 1.0f), + new PointF(3.0f, 1.5f), new PointF(1.0f, 1.5f), new PointF(1.5f, 1.0f), + }; + + byte[] expectedTypes = new byte[] { 0, 1, 1, 129, 0, 1, 1, 1, 1, 1, 1, 129 }; + + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Blue)) + { + gp.AddRectangle(new Rectangle(1, 1, 2, 2)); + Assert.Equal(4, gp.PointCount); + gp.Widen(pen); + Assert.Equal(12, gp.PointCount); + AssertPointsSequenceEqual(expectedPoints, gp.PathPoints, Delta); + Assert.Equal(expectedTypes, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Widen_EmptyPath_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Blue)) + { + Assert.Equal(0, gp.PointCount); + gp.Widen(pen); + Assert.Equal(0, gp.PointCount); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Widen_PenNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("pen", () => gp.Widen(null)); + AssertExtensions.Throws("pen", () => gp.Widen(null, new Matrix())); + AssertExtensions.Throws("pen", () => gp.Widen(null, new Matrix(), 0.67f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Widen_MatrixNull_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Blue)) + { + gp.AddPolygon(new Point[3] { new Point(5, 5), new Point(15, 5), new Point(10, 15) }); + gp.Widen(pen, null); + Assert.Equal(9, gp.PointCount); + AssertWiden3(gp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Widen_MatrixEmpty_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Blue)) + using (Matrix matrix = new Matrix()) + { + gp.AddPolygon(new Point[3] { new Point(5, 5), new Point(15, 5), new Point(10, 15) }); + gp.Widen(pen, new Matrix()); + Assert.Equal(9, gp.PointCount); + AssertWiden3(gp); + } + + } + + public static IEnumerable Widen_PenSmallWidth_TestData() + { + yield return new object[] { new Rectangle(1, 1, 2, 2), 0f, new RectangleF(0.5f, 0.5f, 3.0f, 3.0f) }; + yield return new object[] { new Rectangle(1, 1, 2, 2), 0.5f, new RectangleF(0.5f, 0.5f, 3.0f, 3.0f) }; + yield return new object[] { new Rectangle(1, 1, 2, 2), 1.0f, new RectangleF(0.5f, 0.5f, 3.0f, 3.0f) }; + yield return new object[] { new Rectangle(1, 1, 2, 2), 1.1f, new RectangleF(0.45f, 0.45f, 3.10f, 3.10f) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Widen_PenSmallWidth_TestData))] + public void Widen_Pen_SmallWidth_Success( + Rectangle rectangle, float penWidth, RectangleF expectedBounds) + { + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Aqua, 0)) + using (Matrix matrix = new Matrix()) + { + pen.Width = penWidth; + gp.AddRectangle(rectangle); + gp.Widen(pen); + AssertRectangleEqual(expectedBounds, gp.GetBounds(null), Delta); + AssertRectangleEqual(expectedBounds, gp.GetBounds(matrix), Delta); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_PenNull_ThrowsArgumentNullException() + { + using (GraphicsPath gp = new GraphicsPath()) + { + AssertExtensions.Throws("pen", () => gp.IsOutlineVisible(1, 1, null)); + AssertExtensions.Throws("pen", () => gp.IsOutlineVisible(1.0f, 1.0f, null)); + AssertExtensions.Throws("pen", () => gp.IsOutlineVisible(new Point(), null)); + AssertExtensions.Throws("pen", () => gp.IsOutlineVisible(new PointF(), null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineWithoutGraphics_ReturnsExpected() + { + AssertIsOutlineVisibleLine(null); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineInsideGraphics_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + AssertIsOutlineVisibleLine(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineOutsideGraphics_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(5, 5)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + AssertIsOutlineVisibleLine(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineWithGraphicsTransform_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + using (Matrix matrix = new Matrix(2, 0, 0, 2, 50, -50)) + { + graphics.Transform = matrix; + AssertIsOutlineVisibleLine(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineWithGraphicsPageUnit_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.PageUnit = GraphicsUnit.Millimeter; + AssertIsOutlineVisibleLine(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_LineWithGraphicsPageScale_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.PageScale = 2.0f; + AssertIsOutlineVisibleLine(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsOutlineVisible_RectangleWithoutGraphics_ReturnsExpected() + { + AssertIsOutlineVisibleRectangle(null); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsVisible_RectangleWithoutGraphics_ReturnsExpected() + { + AssertIsVisibleRectangle(null); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsVisible_RectangleWithGraphics_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(40, 40)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + AssertIsVisibleRectangle(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsVisible_EllipseWithoutGraphics_ReturnsExpected() + { + AssertIsVisibleEllipse(null); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsVisible_EllipseWithGraphics_ReturnsExpected() + { + using (Bitmap bitmap = new Bitmap(40, 40)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + AssertIsVisibleEllipse(graphics); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Arc_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddArc(1f, 1f, 2f, 2f, Pi4, Pi4); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Bezier_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddBezier(1, 2, 3, 4, 5, 6, 7, 8); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + public static IEnumerable Reverse_TestData() + { + yield return new object[] + { + new Point[] + { + new Point (1,2), new Point (3,4), new Point (5,6), new Point (7,8), + new Point (9,10), new Point (11,12), new Point (13,14) + } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Reverse_TestData))] + public void Reverse_Beziers_Success(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddBeziers(points); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Reverse_TestData))] + public void Reverse_ClosedCurve_Success(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddClosedCurve(points); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Reverse_TestData))] + public void Reverse_Curve_Success(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddCurve(points); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Ellipse_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddEllipse(1, 2, 3, 4); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Line_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 2, 3, 4); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_LineClosed_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(1, 2, 3, 4); + gp.CloseFigure(); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Reverse_TestData))] + public void Reverse_Lines_Success(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLines(points); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Reverse_TestData))] + public void Reverse_Polygon_Success(Point[] points) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddPolygon(points); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Rectangle_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(1, 2, 3, 4)); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Rectangles_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + Rectangle[] rects = new Rectangle[] { new Rectangle(1, 2, 3, 4), new Rectangle(5, 6, 7, 8) }; + gp.AddRectangles(rects); + AssertReverse(gp, gp.PathPoints, gp.PathTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Pie_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddPie(1, 2, 3, 4, 10, 20); + byte[] expectedTypes = new byte[] { 0, 3, 3, 3, 129 }; + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_ArcLineInnerPath_Success() + { + using (GraphicsPath inner = new GraphicsPath()) + using (GraphicsPath gp = new GraphicsPath()) + { + inner.AddArc(1f, 1f, 2f, 2f, Pi4, Pi4); + inner.AddLine(1, 2, 3, 4); + byte[] expectedTypes = new byte[] { 0, 1, 1, 3, 3, 3 }; + gp.AddPath(inner, true); + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_EllipseRectangle_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddEllipse(50, 51, 50, 100); + gp.AddRectangle(new Rectangle(200, 201, 60, 61)); + byte[] expectedTypes = new byte[] { 0, 1, 1, 129, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 131 }; + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_String_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddString("Mono::", FontFamily.GenericMonospace, 0, 10, new Point(10, 10), StringFormat.GenericDefault); + byte[] expectedTypes = new byte[] + { + 0,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,129, + 0,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,161, + 0,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,129, + 0,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,161, + 0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,131,0,3, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,163,0,3,3,3, + 3,3,3,3,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3,3, + 3,3,3,3,3,3,3,3,3,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3, + 3,3,3,3,3,3,3,3,3,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,3,3,3,3, + 3,3,3,3,3,3,3,3,3,3,3,161,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,3,3,131,0,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,3,3,3,3,163,0,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,3,3, + 3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1, + 1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,3,3,3,3,3,3,3,3,3,3,3,3, + 1,3,3,3,3,3,3,3,3,3,3,3,3,1,1,1,1,129 + }; + + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_Marker_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(200, 201, 60, 61)); + gp.SetMarkers(); + byte[] expectedTypes = new byte[] { 0, 1, 1, 129 }; + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reverse_SubpathMarker_Success() + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(0, 1, 2, 3); + gp.SetMarkers(); + gp.CloseFigure(); + gp.AddBezier(5, 6, 7, 8, 9, 10, 11, 12); + gp.CloseFigure(); + byte[] expectedTypes = new byte[] { 0, 3, 3, 163, 0, 129 }; + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(0, 1, 2, 3); + gp.SetMarkers(); + gp.StartFigure(); + gp.AddLine(20, 21, 22, 23); + gp.AddBezier(5, 6, 7, 8, 9, 10, 11, 12); + byte[] expectedTypes = new byte[] { 0, 3, 3, 3, 1, 33, 0, 1 }; + AssertReverse(gp, gp.PathPoints, expectedTypes); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PointsTypes_Success() + { + int dX = 520; + int dY = 320; + Point[] expectedPoints = new Point[] + { + new Point(dX-64, dY-24), new Point(dX-59, dY-34), new Point(dX-52, dY-54), + new Point(dX-18, dY-66), new Point(dX-34, dY-47), new Point(dX-43, dY-27), + new Point(dX-44, dY-8), + }; + + byte[] expectedTypes = new byte[] + { + (byte)PathPointType.Start, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, + (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, (byte)PathPointType.Bezier, + (byte)PathPointType.Bezier + }; + + using (GraphicsPath path = new GraphicsPath(expectedPoints, expectedTypes)) + { + Assert.Equal(7, path.PointCount); + byte[] actualTypes = path.PathTypes; + Assert.Equal(expectedTypes, actualTypes); + } + } + + private void AssertEmptyGraphicsPath(GraphicsPath gp) + { + Assert.Equal(0, gp.PathData.Points.Length); + Assert.Equal(0, gp.PathData.Types.Length); + Assert.Equal(0, gp.PointCount); + } + + private void AssertEqual(float expexted, float actual, float tollerance) + { + AssertExtensions.LessThanOrEqualTo(Math.Abs(expexted - actual), tollerance); + } + + private void AssertLine(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(1f, 1f), new PointF(2f, 2f) + }; + + Assert.Equal(2, path.PathPoints.Length); + Assert.Equal(2, path.PathTypes.Length); + Assert.Equal(2, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 1f, 1f), path.GetBounds()); + Assert.Equal(expectedPoints, path.PathPoints); + Assert.Equal(new byte[] { 0, 1 }, path.PathTypes); + } + + private void AssertArc(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(2.99990582f, 2.01370716f), new PointF(2.99984312f, 2.018276f), + new PointF(2.99974918f, 2.02284455f), new PointF(2.999624f, 2.027412f), + }; + + Assert.Equal(4, path.PathPoints.Length); + Assert.Equal(4, path.PathTypes.Length); + Assert.Equal(4, path.PathData.Points.Length); + Assert.Equal(new RectangleF(2.99962401f, 2.01370716f, 0f, 0.0137047768f), path.GetBounds()); + Assert.Equal(expectedPoints, path.PathPoints); + Assert.Equal(new byte[] { 0, 3, 3, 3 }, path.PathTypes); + } + + private void AssertBezier(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(1f, 1f), new PointF(2f, 2f), + new PointF(3f, 3f), new PointF(4f, 4f), + }; + + Assert.Equal(4, path.PointCount); + Assert.Equal(4, path.PathPoints.Length); + Assert.Equal(4, path.PathTypes.Length); + Assert.Equal(4, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 3f, 3f), path.GetBounds()); + Assert.Equal(expectedPoints, path.PathPoints); + Assert.Equal(new byte[] { 0, 3, 3, 3 }, path.PathTypes); + } + + private void AssertCurve(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(1f, 1f), new PointF(1.16666663f, 1.16666663f), + new PointF(1.83333325f, 1.83333325f), new PointF(2f, 2f) + }; + + Assert.Equal(4, path.PathPoints.Length); + Assert.Equal(4, path.PathTypes.Length); + Assert.Equal(4, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 1f, 1f), path.GetBounds()); + AssertPointsSequenceEqual(expectedPoints, path.PathPoints, Delta); + Assert.Equal(new byte[] { 0, 3, 3, 3 }, path.PathTypes); + } + + private void AssertClosedCurve(GraphicsPath path) + { + Assert.Equal(10, path.PathPoints.Length); + Assert.Equal(10, path.PathTypes.Length); + Assert.Equal(10, path.PathData.Points.Length); + Assert.Equal(new RectangleF(0.8333333f, 0.8333333f, 2.33333278f, 2.33333278f), path.GetBounds()); + Assert.Equal(new byte[] { 0, 3, 3, 3, 3, 3, 3, 3, 3, 131 }, path.PathTypes); + } + + private void AssertRectangle(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(1f, 1f), new PointF(3f, 1f), + new PointF(3f, 3f), new PointF(1f, 3f) + }; + + Assert.Equal(4, path.PathPoints.Length); + Assert.Equal(4, path.PathTypes.Length); + Assert.Equal(4, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 2f, 2f), path.GetBounds()); + Assert.Equal(expectedPoints, path.PathPoints); + Assert.Equal(new byte[] { 0, 1, 1, 129 }, path.PathTypes); + } + + private void AssertEllipse(GraphicsPath path) + { + Assert.Equal(13, path.PathPoints.Length); + Assert.Equal(13, path.PathTypes.Length); + Assert.Equal(13, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 2f, 2f), path.GetBounds()); + Assert.Equal(new byte[] { 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 131 }, path.PathTypes); + } + + private void AssertPie(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(2f, 2f), new PointF(2.99990582f, 2.01370716f), + new PointF(2.99984312f, 2.018276f), new PointF(2.99974918f, 2.02284455f), + new PointF(2.999624f, 2.027412f) + }; + + Assert.Equal(5, path.PathPoints.Length); + Assert.Equal(5, path.PathTypes.Length); + Assert.Equal(5, path.PathData.Points.Length); + AssertRectangleEqual(new RectangleF(2f, 2f, 0.9999058f, 0.0274119377f), path.GetBounds(), Delta); + AssertPointsSequenceEqual(expectedPoints, path.PathPoints, Delta); + Assert.Equal(new byte[] { 0, 1, 3, 3, 131 }, path.PathTypes); + } + + private void AssertPolygon(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(1f, 1f), + new PointF(2f, 2f), + new PointF(3f, 3f) + }; + + Assert.Equal(3, path.PathPoints.Length); + Assert.Equal(3, path.PathTypes.Length); + Assert.Equal(3, path.PathData.Points.Length); + Assert.Equal(new RectangleF(1f, 1f, 2f, 2f), path.GetBounds()); + Assert.Equal(expectedPoints, path.PathPoints); + Assert.Equal(new byte[] { 0, 1, 129 }, path.PathTypes); + } + + private void AssertFlats(GraphicsPath flat, GraphicsPath original) + { + AssertExtensions.GreaterThanOrEqualTo(flat.PointCount, original.PointCount); + for (int i = 0; i < flat.PointCount; i++) + { + Assert.NotEqual(3, flat.PathTypes[i]); + } + } + + private void AssertWrapNaN(GraphicsPath path) + { + byte[] expectedTypes = new byte[] { 0, 1, 129 }; + + Assert.Equal(3, path.PointCount); + Assert.Equal(float.NaN, path.PathPoints[0].X); + Assert.Equal(float.NaN, path.PathPoints[0].Y); + Assert.Equal(float.NaN, path.PathPoints[1].X); + Assert.Equal(float.NaN, path.PathPoints[1].Y); + Assert.Equal(float.NaN, path.PathPoints[2].X); + Assert.Equal(float.NaN, path.PathPoints[2].Y); + Assert.Equal(expectedTypes, path.PathTypes); + } + + private void AssertWiden3(GraphicsPath path) + { + PointF[] expectedPoints = new PointF[] + { + new PointF(4.2f, 4.5f), new PointF(15.8f, 4.5f), + new PointF(10.0f, 16.1f), new PointF(10.4f, 14.8f), + new PointF(9.6f, 14.8f), new PointF(14.6f, 4.8f), + new PointF(15.0f, 5.5f), new PointF(5.0f, 5.5f), + new PointF(5.4f, 4.8f) + }; + + AssertPointsSequenceEqual(expectedPoints, path.PathPoints, 0.25f); + Assert.Equal(new byte[] { 0, 1, 129, 0, 1, 1, 1, 1, 129 }, path.PathTypes); + } + + private void AssertIsOutlineVisibleLine(Graphics graphics) + { + using (GraphicsPath gp = new GraphicsPath()) + using (Pen pen = new Pen(Color.Red, 3.0f)) + { + gp.AddLine(10, 1, 14, 1); + Assert.True(gp.IsOutlineVisible(10, 1, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(10, 2, pen, graphics)); + Assert.False(gp.IsOutlineVisible(10, 2, Pens.Red, graphics)); + + Assert.True(gp.IsOutlineVisible(11.0f, 1.0f, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(11.0f, 1.0f, pen, graphics)); + Assert.False(gp.IsOutlineVisible(11.0f, 2.0f, Pens.Red, graphics)); + + Point point = new Point(12, 2); + Assert.False(gp.IsOutlineVisible(point, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(point, pen, graphics)); + + point.Y = 1; + Assert.True(gp.IsOutlineVisible(point, Pens.Red, graphics)); + + PointF fPoint = new PointF(13.0f, 2.0f); + Assert.False(gp.IsOutlineVisible(fPoint, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(fPoint, pen, graphics)); + + fPoint.Y = 1; + Assert.True(gp.IsOutlineVisible(fPoint, Pens.Red, graphics)); + } + } + + private void AssertIsOutlineVisibleRectangle(Graphics graphics) + { + using (Pen pen = new Pen(Color.Red, 3.0f)) + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(10, 10, 20, 20)); + Assert.True(gp.IsOutlineVisible(10, 10, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(10, 11, pen, graphics)); + Assert.False(gp.IsOutlineVisible(11, 11, Pens.Red, graphics)); + + Assert.True(gp.IsOutlineVisible(11.0f, 10.0f, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(11.0f, 11.0f, pen, graphics)); + Assert.False(gp.IsOutlineVisible(11.0f, 11.0f, Pens.Red, graphics)); + + Point point = new Point(15, 10); + Assert.True(gp.IsOutlineVisible(point, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(point, pen, graphics)); + + point.Y = 15; + Assert.False(gp.IsOutlineVisible(point, Pens.Red, graphics)); + + PointF fPoint = new PointF(29.0f, 29.0f); + Assert.False(gp.IsOutlineVisible(fPoint, Pens.Red, graphics)); + Assert.True(gp.IsOutlineVisible(fPoint, pen, graphics)); + + fPoint.Y = 31.0f; + Assert.True(gp.IsOutlineVisible(fPoint, pen, graphics)); + } + } + + private void AssertIsVisibleRectangle(Graphics graphics) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddRectangle(new Rectangle(10, 10, 20, 20)); + Assert.False(gp.IsVisible(9, 9, graphics)); + Assert.True(gp.IsVisible(10, 10, graphics)); + Assert.True(gp.IsVisible(20, 20, graphics)); + Assert.True(gp.IsVisible(29, 29, graphics)); + Assert.False(gp.IsVisible(30, 29, graphics)); + Assert.False(gp.IsVisible(29, 30, graphics)); + Assert.False(gp.IsVisible(30, 30, graphics)); + Assert.False(gp.IsVisible(9.4f, 9.4f, graphics)); + Assert.True(gp.IsVisible(9.5f, 9.5f, graphics)); + Assert.True(gp.IsVisible(10f, 10f, graphics)); + Assert.True(gp.IsVisible(20f, 20f, graphics)); + Assert.True(gp.IsVisible(29.4f, 29.4f, graphics)); + Assert.False(gp.IsVisible(29.5f, 29.5f, graphics)); + Assert.False(gp.IsVisible(29.5f, 29.4f, graphics)); + Assert.False(gp.IsVisible(29.4f, 29.5f, graphics)); + } + } + + private void AssertIsVisibleEllipse(Graphics graphics) + { + using (GraphicsPath gp = new GraphicsPath()) + { + gp.AddEllipse(new Rectangle(10, 10, 20, 20)); + Assert.False(gp.IsVisible(10, 10, graphics)); + Assert.True(gp.IsVisible(20, 20, graphics)); + Assert.False(gp.IsVisible(29, 29, graphics)); + Assert.False(gp.IsVisible(10f, 10f, graphics)); + Assert.True(gp.IsVisible(20f, 20f, graphics)); + Assert.False(gp.IsVisible(29.4f, 29.4f, graphics)); + } + } + + private void AssertReverse(GraphicsPath gp, PointF[] expectedPoints, byte[] expectedTypes) + { + gp.Reverse(); + PointF[] reversedPoints = gp.PathPoints; + byte[] reversedTypes = gp.PathTypes; + + int count = gp.PointCount; + Assert.Equal(expectedPoints.Length, gp.PointCount); + Assert.Equal(expectedTypes, gp.PathTypes); + for (int i = 0; i < count; i++) + { + Assert.Equal(expectedPoints[i], reversedPoints[count - i - 1]); + Assert.Equal(expectedTypes[i], reversedTypes[i]); + } + } + + private void AssertPointsSequenceEqual(PointF[] expected, PointF[] actual, float tolerance) + { + int count = expected.Length; + Assert.Equal(expected.Length, actual.Length); + for (int i = 0; i < count; i++) + { + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected[i].X - actual[i].X), tolerance); + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected[i].Y - actual[i].Y), tolerance); + } + } + + private void AssertRectangleEqual(RectangleF expected, RectangleF actual, float tolerance) + { + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected.X - actual.X), tolerance); + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected.Y - actual.Y), tolerance); + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected.Width - actual.Width), tolerance); + AssertExtensions.LessThanOrEqualTo(Math.Abs(expected.Height - actual.Height), tolerance); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/HatchBrushTests.cs b/src/System.Drawing.Common/tests/Drawing2D/HatchBrushTests.cs new file mode 100644 index 00000000000..09aeae84fe2 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/HatchBrushTests.cs @@ -0,0 +1,132 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class HatchBrushTests + { + public static IEnumerable Ctor_HatchStyle_ForeColor_TestData() + { + yield return new object[] { HatchStyle.Horizontal, new Color() }; + yield return new object[] { HatchStyle.SolidDiamond, Color.PapayaWhip }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_HatchStyle_ForeColor_TestData))] + public void Ctor_HatchStyle_ForeColor(HatchStyle hatchStyle, Color foreColor) + { + using (var brush = new HatchBrush(hatchStyle, foreColor)) + { + Assert.Equal(hatchStyle, brush.HatchStyle); + + Assert.NotEqual(foreColor, brush.ForegroundColor); + Assert.Equal(foreColor.ToArgb(), brush.ForegroundColor.ToArgb()); + + Assert.Equal(Color.FromArgb(255, 0, 0, 0), brush.BackgroundColor); + } + } + + public static IEnumerable Ctor_HatchStyle_ForeColor_BackColor_TestData() + { + yield return new object[] { HatchStyle.Horizontal, new Color(), new Color() }; + yield return new object[] { HatchStyle.SolidDiamond, Color.PapayaWhip, Color.Plum }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_HatchStyle_ForeColor_BackColor_TestData))] + public void Ctor_HatchStyle_ForeColor_BackColor(HatchStyle hatchStyle, Color foreColor, Color backColor) + { + using (var brush = new HatchBrush(hatchStyle, foreColor, backColor)) + { + Assert.Equal(hatchStyle, brush.HatchStyle); + + Assert.NotEqual(foreColor, brush.ForegroundColor); + Assert.Equal(foreColor.ToArgb(), brush.ForegroundColor.ToArgb()); + + Assert.NotEqual(backColor, brush.BackgroundColor); + Assert.Equal(backColor.ToArgb(), brush.BackgroundColor.ToArgb()); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(HatchStyle.Horizontal -1 )] + [InlineData(HatchStyle.SolidDiamond + 1)] + public void Ctor_InvalidHatchStyle_ThrowsArgumentException(HatchStyle hatchStyle) + { + AssertExtensions.Throws("hatchstyle", null, () => new HatchBrush(hatchStyle, Color.Empty)); + AssertExtensions.Throws("hatchstyle", null, () => new HatchBrush(hatchStyle, Color.Empty, Color.Empty)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Brush_ReturnsClone() + { + using (var brush = new HatchBrush(HatchStyle.DarkDownwardDiagonal, Color.Magenta, Color.Peru)) + { + HatchBrush clone = Assert.IsType(brush.Clone()); + + Assert.NotSame(clone, brush); + Assert.Equal(brush.HatchStyle, clone.HatchStyle); + Assert.Equal(brush.ForegroundColor, clone.ForegroundColor); + Assert.Equal(brush.BackgroundColor, clone.BackgroundColor); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_ImmutableColor_ReturnsMutableClone() + { + SolidBrush brush = Assert.IsType(Brushes.Bisque); + SolidBrush clone = Assert.IsType(brush.Clone()); + + clone.Color = SystemColors.AppWorkspace; + Assert.Equal(SystemColors.AppWorkspace, clone.Color); + Assert.Equal(Color.Bisque, brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var brush = new HatchBrush(HatchStyle.DarkHorizontal, Color.PeachPuff, Color.Purple); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Clone()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void HatchStyle_EmptyAndGetDisposed_ThrowsArgumentException() + { + var brush = new HatchBrush(HatchStyle.DarkHorizontal, Color.PeachPuff, Color.Purple); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.HatchStyle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ForegroundColor_EmptyAndGetDisposed_ThrowsArgumentException() + { + var brush = new HatchBrush(HatchStyle.DarkHorizontal, Color.PeachPuff, Color.Purple); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ForegroundColor); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void BackgroundColor_EmptyAndGetDisposed_ThrowsArgumentException() + { + var brush = new HatchBrush(HatchStyle.DarkHorizontal, Color.PeachPuff, Color.Purple); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.BackgroundColor); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Success() + { + var brush = new HatchBrush(HatchStyle.DarkHorizontal, Color.PeachPuff, Color.Purple); + brush.Dispose(); + brush.Dispose(); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/LinearGradientBrushTests.cs b/src/System.Drawing.Common/tests/Drawing2D/LinearGradientBrushTests.cs new file mode 100644 index 00000000000..d60b0e5b439 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/LinearGradientBrushTests.cs @@ -0,0 +1,1111 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class LinearGradientBrushTests + { + public static IEnumerable Ctor_Point_TestData() + { + yield return new object[] { new Point(0, 0), new Point(2, 2), Color.Empty, Color.Empty, new RectangleF(0, 0, 2, 2) }; + yield return new object[] { new Point(1, 0), new Point(0, 0), Color.Empty, Color.Red, new RectangleF(0, -0.5f, 1, 1) }; + yield return new object[] { new Point(1, 2), new Point(4, 6), Color.Plum, Color.Red, new RectangleF(1, 2, 3, 4) }; + yield return new object[] { new Point(1, 2), new Point(4, 6), Color.Red, Color.Red, new RectangleF(1, 2, 3, 4) }; + yield return new object[] { new Point(-1, -2), new Point(4, 6), Color.Red, Color.Plum, new RectangleF(-1, -2, 5, 8) }; + yield return new object[] { new Point(-4, -6), new Point(1, 2), Color.Black, Color.Wheat, new RectangleF(-4, -6, 5, 8) }; + yield return new object[] { new Point(4, 6), new Point(-1, -2), Color.Black, Color.Wheat, new RectangleF(-1, -2, 5, 8) }; + yield return new object[] { new Point(4, 6), new Point(1, 2), Color.Black, Color.Wheat, new RectangleF(1, 2, 3, 4) }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Ctor_Point_TestData))] + public void Ctor_PointF_PointF_Color_Color(Point point1, Point point2, Color color1, Color color2, RectangleF expectedRectangle) + { + using (var brush = new LinearGradientBrush((PointF)point1, point2, color1, color2)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(expectedRectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.False(brush.Transform.IsIdentity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PointF_PointF_Color_Color_FloatRanges() + { + using (var brush = new LinearGradientBrush(new PointF(float.NaN, float.NaN), new PointF(float.PositiveInfinity, float.NegativeInfinity), Color.Plum, Color.Red)) + { + Assert.Equal(float.PositiveInfinity, brush.Rectangle.X); + Assert.Equal(float.NegativeInfinity, brush.Rectangle.Y); + Assert.Equal(float.NaN, brush.Rectangle.Width); + Assert.Equal(float.NaN, brush.Rectangle.Height); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Ctor_Point_TestData))] + public void Ctor_Point_Point_Color_Color(Point point1, Point point2, Color color1, Color color2, RectangleF expectedRectangle) + { + using (var brush = new LinearGradientBrush(point1, point2, color1, color2)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(expectedRectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.False(brush.Transform.IsIdentity); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(0, 0)] + [InlineData(1, 1)] + public void Ctor_EqualPoints_ThrowsOutOfMemoryException(int x, int y) + { + Assert.Throws(() => new LinearGradientBrush(new Point(x, y), new Point(x, y), Color.Fuchsia, Color.GhostWhite)); + Assert.Throws(() => new LinearGradientBrush(new PointF(x, y), new PointF(x, y), Color.Fuchsia, Color.GhostWhite)); + } + + public static IEnumerable Ctor_Rectangle_LinearGradientMode_TestData() + { + yield return new object[] { new Rectangle(0, 0, 1, 2), Color.Empty, Color.Red, LinearGradientMode.BackwardDiagonal }; + yield return new object[] { new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, LinearGradientMode.ForwardDiagonal }; + yield return new object[] { new Rectangle(-1, -2, -3, -4), Color.Red, Color.Red, LinearGradientMode.Horizontal }; + yield return new object[] { new Rectangle(1, 2, 3, 4), Color.Red, Color.Plum, LinearGradientMode.Vertical }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_LinearGradientMode_TestData))] + public void Ctor_Rectangle_Color_Color_LinearGradientMode(Rectangle rectangle, Color color1, Color color2, LinearGradientMode linearGradientMode) + { + using (var brush = new LinearGradientBrush(rectangle, color1, color2, linearGradientMode)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal(linearGradientMode == LinearGradientMode.Horizontal, brush.Transform.IsIdentity); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_LinearGradientMode_TestData))] + public void Ctor_RectangleF_Color_Color_LinearGradientMode(Rectangle rectangle, Color color1, Color color2, LinearGradientMode linearGradientMode) + { + using (var brush = new LinearGradientBrush((RectangleF)rectangle, color1, color2, linearGradientMode)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal(linearGradientMode == LinearGradientMode.Horizontal, brush.Transform.IsIdentity); + } + } + + public static IEnumerable Ctor_Rectangle_Angle_TestData() + { + yield return new object[] { new Rectangle(0, 0, 1, 2), Color.Empty, Color.Red, 90 }; + yield return new object[] { new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 180 }; + yield return new object[] { new Rectangle(-1, -2, -3, -4), Color.Red, Color.Red, 0 }; + yield return new object[] { new Rectangle(-1, -2, -3, -4), Color.Red, Color.Red, 360 }; + yield return new object[] { new Rectangle(1, 2, 3, 4), Color.Red, Color.Plum, 90 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Angle_TestData))] + public void Ctor_Rectangle_Color_Color_Angle(Rectangle rectangle, Color color1, Color color2, float angle) + { + using (var brush = new LinearGradientBrush(rectangle, color1, color2, angle)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal((angle % 360) == 0, brush.Transform.IsIdentity); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Angle_TestData))] + public void Ctor_RectangleF_Color_Color_Angle(Rectangle rectangle, Color color1, Color color2, float angle) + { + using (var brush = new LinearGradientBrush((RectangleF)rectangle, color1, color2, angle)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal((angle % 360) == 0, brush.Transform.IsIdentity); + } + } + + public static IEnumerable Ctor_Rectangle_Angle_IsAngleScalable_TestData() + { + foreach (object[] testData in Ctor_Rectangle_Angle_TestData()) + { + yield return new object[] { testData[0], testData[1], testData[2], testData[3], true }; + yield return new object[] { testData[0], testData[1], testData[2], testData[3], false }; + } + } + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Angle_IsAngleScalable_TestData))] + public void Ctor_Rectangle_Color_Color_Angle_IsAngleScalable(Rectangle rectangle, Color color1, Color color2, float angle, bool isAngleScalable) + { + using (var brush = new LinearGradientBrush(rectangle, color1, color2, angle, isAngleScalable)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal((angle % 360) == 0, brush.Transform.IsIdentity); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Angle_IsAngleScalable_TestData))] + public void Ctor_RectangleF_Color_Color_Angle_IsAngleScalable(Rectangle rectangle, Color color1, Color color2, float angle, bool isAngleScalable) + { + using (var brush = new LinearGradientBrush((RectangleF)rectangle, color1, color2, angle, isAngleScalable)) + { + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + + Assert.False(brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + Assert.Equal(new Color[] { Color.FromArgb(color1.ToArgb()), Color.FromArgb(color2.ToArgb()) }, brush.LinearColors); + Assert.Equal(rectangle, brush.Rectangle); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + + Assert.Equal((angle % 360) == 0, brush.Transform.IsIdentity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_ZeroWidth_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 0, 4), Color.Empty, Color.Empty, 0f)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 0, 4), Color.Empty, Color.Empty, 0f)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 0, 4), Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 0, 4), Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 0, 4), Color.Empty, Color.Empty, 0, true)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 0, 4), Color.Empty, Color.Empty, 0, true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_ZeroHeight_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 3, 0), Color.Empty, Color.Empty, 0f)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 3, 0), Color.Empty, Color.Empty, 0f)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 3, 0), Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 3, 0), Color.Empty, Color.Empty, LinearGradientMode.BackwardDiagonal)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new Rectangle(1, 2, 3, 0), Color.Empty, Color.Empty, 0, true)); + AssertExtensions.Throws(null, () => new LinearGradientBrush(new RectangleF(1, 2, 3, 0), Color.Empty, Color.Empty, 0, true)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LinearGradientMode.Horizontal - 1)] + [InlineData(LinearGradientMode.BackwardDiagonal + 1)] + public void Ctor_InvalidLinearGradientMode_ThrowsEnumArgumentException(LinearGradientMode linearGradientMode) + { + Assert.ThrowsAny(() => new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Empty, Color.Empty, linearGradientMode)); + Assert.ThrowsAny(() => new LinearGradientBrush(new RectangleF(1, 2, 3, 4), Color.Empty, Color.Empty, linearGradientMode)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Brush_ReturnsClone() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + LinearGradientBrush clone = Assert.IsType(brush.Clone()); + + Assert.NotSame(clone, brush); + Assert.Equal(brush.Blend.Factors, clone.Blend.Factors); + Assert.Equal(brush.Blend.Positions.Length, clone.Blend.Positions.Length); + Assert.Equal(brush.LinearColors, clone.LinearColors); + Assert.Equal(brush.Rectangle, clone.Rectangle); + Assert.Equal(brush.Transform, clone.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Clone()); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Blend_GetWithInterpolationColorsSet_ReturnsNull() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + var blend = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 10, 1 } + }; + + brush.InterpolationColors = blend; + Assert.Null(brush.Blend); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(new float[] { 1 }, new float[] { 1 })] + [InlineData(new float[] { 0 }, new float[] { 0 })] + [InlineData(new float[] { float.MaxValue }, new float[] { float.MaxValue })] + [InlineData(new float[] { float.MinValue }, new float[] { float.MinValue })] + [InlineData(new float[] { 0.5f, 0.5f }, new float[] { 0, 1 })] + [InlineData(new float[] { 0.4f, 0.3f, 0.2f }, new float[] { 0, 0.5f, 1 })] + [InlineData(new float[] { -1 }, new float[] { -1 })] + [InlineData(new float[] { float.NaN }, new float[] { float.NaN })] + [InlineData(new float[] { 1 }, new float[] { 1, 2 })] + public void Blend_Set_Success(float[] factors, float[] positions) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + var blend = new Blend + { + Factors = factors, + Positions = positions + }; + brush.Blend = blend; + + Assert.Equal(blend.Factors, brush.Blend.Factors); + Assert.Equal(factors.Length, brush.Blend.Positions.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { 1, 2 }, new float[] { 1, 2 })] + [InlineData(new float[] { 1, 2 }, new float[] { 1, 1 })] + [InlineData(new float[] { 1, 2 }, new float[] { 1, 0 })] + [InlineData(new float[] { 0, 0, 0 }, new float[] { 0, 0, 0 })] + public void Blend_InvalidBlend_ThrowsArgumentException(float[] factors, float[] positions) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + var blend = new Blend + { + Factors = factors, + Positions = positions + }; + AssertExtensions.Throws(null, () => brush.Blend = blend); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_SetNullBlend_ThrowsNullReferenceException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.Blend = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_SetNullBlendFactors_ThrowsNullReferenceException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.Blend = new Blend { Factors = null }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_SetNullBlendPositions_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("value", "source", () => brush.Blend = new Blend { Factors = new float[2], Positions = null }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_SetFactorsLengthGreaterThanPositionsLength_ThrowsArgumentOutOfRangeException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("value", null, () => brush.Blend = new Blend { Factors = new float[2], Positions = new float[1] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_SetInvalidBlendFactorsLength_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.Blend = new Blend { Factors = new float[0], Positions = new float[0] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Blend); + AssertExtensions.Throws(null, () => brush.Blend = new Blend()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(true)] + [InlineData(false)] + public void GammaCorrection_Set_GetReturnsExpected(bool gammaCorrection) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) { GammaCorrection = gammaCorrection }) + { + Assert.Equal(gammaCorrection, brush.GammaCorrection); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GammaCorrection_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.GammaCorrection); + AssertExtensions.Throws(null, () => brush.GammaCorrection = true); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetValid_GetReturnsExpected() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + var blend = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 10, 1 } + }; + + brush.InterpolationColors = blend; + Assert.Equal(blend.Colors.Select(c => Color.FromArgb(c.ToArgb())), brush.InterpolationColors.Colors); + Assert.Equal(blend.Positions, brush.InterpolationColors.Positions); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetWithExistingInterpolationColors_OverwritesInterpolationColors() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) + { + InterpolationColors = new ColorBlend + { + Colors = new Color[] { Color.Wheat, Color.Yellow }, + Positions = new float[] { 0, 1 } + } + }) + { + var blend = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 0.5f, 1f } + }; + brush.InterpolationColors = blend; + Assert.Equal(blend.Colors.Select(c => Color.FromArgb(c.ToArgb())), brush.InterpolationColors.Colors); + Assert.Equal(blend.Positions, brush.InterpolationColors.Positions); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetNullBlend_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.InterpolationColors = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetBlendWithNullColors_ThrowsNullReferenceException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.InterpolationColors = new ColorBlend { Colors = null }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void InterpolationColors_SetBlendWithTooFewColors_ThrowsArgumentException(int colorsLength) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.InterpolationColors = new ColorBlend { Colors = new Color[colorsLength] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetNullBlendPositions_ThrowsNullReferenceException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.InterpolationColors = new ColorBlend { Colors = new Color[2], Positions = null }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(3)] + public void InterpolationColors_SetInvalidBlendPositionsLength_ThrowsArgumentException(int positionsLength) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.InterpolationColors = new ColorBlend + { + Colors = new Color[2], + Positions = new float[positionsLength] + }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { 1, 0, 1 })] + [InlineData(new float[] { 0, 0, 0 })] + public void InterpolationColors_InvalidPositions_ThrowsArgumentException(float[] positions) + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + AssertExtensions.Throws(null, () => brush.InterpolationColors = new ColorBlend + { + Colors = new Color[positions.Length], + Positions = positions + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) + { + InterpolationColors = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 0.5f, 1 } + } + }; + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.InterpolationColors); + AssertExtensions.Throws(null, () => brush.InterpolationColors = new ColorBlend + { + Colors = new Color[2], + Positions = new float[] { 0f, 1f } + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_SetBlendTriangularShape_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) + { + InterpolationColors = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 0.5f, 1 } + } + }) + { + Assert.NotNull(brush.InterpolationColors); + + brush.SetBlendTriangularShape(0.5f); + AssertExtensions.Throws(null, () => brush.InterpolationColors); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void InterpolationColors_SetBlend_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) + { + InterpolationColors = new ColorBlend + { + Colors = new Color[] { Color.Red, Color.PeachPuff, Color.PowderBlue }, + Positions = new float[] { 0, 0.5f, 1 } + } + }) + { + Assert.NotNull(brush.InterpolationColors); + + brush.Blend = new Blend + { + Factors = new float[1], + Positions = new float[1] + }; + AssertExtensions.Throws(null, () => brush.InterpolationColors); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LinearColors_SetValid_GetReturnsExpected() + { + Color[] colors = new Color[] { Color.Red, Color.Blue, Color.AntiqueWhite }; + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) { LinearColors = colors }) + { + Assert.Equal(colors.Take(2).Select(c => Color.FromArgb(c.ToArgb())), brush.LinearColors); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LinearColors_SetNull_ThrowsNullReferenceException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.LinearColors = null); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void LinearColors_SetInvalidLength_ThrowsIndexOutOfRangeException(int length) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.Throws(() => brush.LinearColors = new Color[length]); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LinearColors_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.LinearColors); + AssertExtensions.Throws(null, () => brush.LinearColors = new Color[] { Color.Red, Color.Wheat }); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Rectangle_GetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Rectangle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetValid_GetReturnsExpected() + { + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) { Transform = transform }) + { + Assert.Equal(transform, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNull_ThrowsArgumentNullException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("value", "matrix", () => brush.Transform = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Transform); + AssertExtensions.Throws(null, () => brush.Transform = new Matrix()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WrapMode.Tile)] + [InlineData(WrapMode.TileFlipX)] + [InlineData(WrapMode.TileFlipXY)] + [InlineData(WrapMode.TileFlipY)] + public void WrapMode_SetValid_GetReturnsExpected(WrapMode wrapMode) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true) { WrapMode = wrapMode }) + { + Assert.Equal(wrapMode, brush.WrapMode); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WrapMode.Tile - 1)] + [InlineData(WrapMode.Clamp + 1)] + public void WrapMode_SetInvalid_ThrowsInvalidEnumArgumentException(WrapMode wrapMode) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.ThrowsAny(() => brush.WrapMode = wrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_Clamp_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.WrapMode = WrapMode.Clamp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_GetSetDisposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.WrapMode); + AssertExtensions.Throws(null, () => brush.WrapMode = WrapMode.TileFlipX); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Invoke_SetsTransformToIdentity() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Assert.False(brush.Transform.IsIdentity); + + brush.ResetTransform(); + Assert.True(brush.Transform.IsIdentity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ResetTransform()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NoOrder_Success() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Multiply(matrix); + + brush.MultiplyTransform(matrix); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(MatrixOrder.Prepend)] + [InlineData(MatrixOrder.Append)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void MultiplyTransform_Order_Success(MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + Matrix expectedTransform = brush.Transform; + + if (order == MatrixOrder.Append || order == MatrixOrder.Prepend) + { + expectedTransform.Multiply(matrix, order); + } + else + { + // Invalid MatrixOrder is interpreted as MatrixOrder.Append. + expectedTransform.Multiply(matrix, MatrixOrder.Append); + } + + brush.MultiplyTransform(matrix, order); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NullMatrix_ThrowsArgumentNullException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null)); + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_DisposedMatrix_Nop() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + using (Matrix transform = brush.Transform) + { + var matrix = new Matrix(); + matrix.Dispose(); + + brush.MultiplyTransform(matrix); + brush.MultiplyTransform(matrix, MatrixOrder.Append); + + Assert.Equal(transform, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NonInvertibleMatrix_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.MultiplyTransform(new Matrix())); + AssertExtensions.Throws(null, () => brush.MultiplyTransform(new Matrix(), MatrixOrder.Prepend)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, -2)] + [InlineData(0, 0)] + [InlineData(1, 2)] + public void TranslateTransform_NoOrder_Success(float dx, float dy) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Translate(dx, dy); + + brush.TranslateTransform(dx, dy); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1, MatrixOrder.Prepend)] + [InlineData(1, 1, MatrixOrder.Append)] + [InlineData(0, 0, MatrixOrder.Prepend)] + [InlineData(0, 0, MatrixOrder.Append)] + [InlineData(-1, -1, MatrixOrder.Prepend)] + [InlineData(-1, -1, MatrixOrder.Append)] + public void TranslateTransform_Order_Success(float dx, float dy, MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Translate(dx, dy, order); + + brush.TranslateTransform(dx, dy, order); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void TranslateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.TranslateTransform(0, 0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.TranslateTransform(0, 0)); + AssertExtensions.Throws(null, () => brush.TranslateTransform(0, 0, MatrixOrder.Append)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, -2)] + [InlineData(0, 0)] + [InlineData(1, 2)] + public void ScaleTransform_NoOrder_Success(float sx, float sy) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Scale(sx, sy); + + brush.ScaleTransform(sx, sy); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1, MatrixOrder.Prepend)] + [InlineData(1, 1, MatrixOrder.Append)] + [InlineData(0, 0, MatrixOrder.Prepend)] + [InlineData(0, 0, MatrixOrder.Append)] + [InlineData(-1, -1, MatrixOrder.Prepend)] + [InlineData(-1, -1, MatrixOrder.Append)] + public void ScaleTransform_Order_Success(float sx, float sy, MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Scale(sx, sy, order); + + brush.ScaleTransform(sx, sy, order); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void ScaleTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.ScaleTransform(0, 0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ScaleTransform(0, 0)); + AssertExtensions.Throws(null, () => brush.ScaleTransform(0, 0, MatrixOrder.Append)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(360)] + public void RotateTransform_NoOrder_Success(float angle) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Rotate(angle); + + brush.RotateTransform(angle); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, MatrixOrder.Prepend)] + [InlineData(1, MatrixOrder.Append)] + [InlineData(0, MatrixOrder.Prepend)] + [InlineData(360, MatrixOrder.Append)] + [InlineData(-1, MatrixOrder.Prepend)] + [InlineData(-1, MatrixOrder.Append)] + public void RotateTransform_Order_Success(float angle, MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + Matrix expectedTransform = brush.Transform; + expectedTransform.Rotate(angle, order); + + brush.RotateTransform(angle, order); + Assert.Equal(expectedTransform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void RotateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws(null, () => brush.RotateTransform(0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.RotateTransform(0)); + AssertExtensions.Throws(null, () => brush.RotateTransform(0, MatrixOrder.Append)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(0.5)] + [InlineData(1)] + [InlineData(float.NaN)] + public void SetSigmalBellShape(float focus) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + brush.SetSigmaBellShape(focus); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-0.1)] + [InlineData(1.1)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void SetSigmalBellShape_InvalidFocus_ThrowsArgumentException(float focus) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("focus", null, () => brush.SetSigmaBellShape(focus)); + AssertExtensions.Throws("focus", null, () => brush.SetSigmaBellShape(focus, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-0.1)] + [InlineData(1.1)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void SetSigmalBellShape_InvalidScale_ThrowsArgumentException(float scale) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("scale", null, () => brush.SetSigmaBellShape(0.1f, scale)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetSigmalBellShape_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.SetSigmaBellShape(0)); + AssertExtensions.Throws(null, () => brush.SetSigmaBellShape(0, 1)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, new float[] { 1, 0 }, new float[] { 0, 1 })] + [InlineData(0.5, new float[] { 0, 1, 0 }, new float[] { 0, 0.5f, 1 })] + [InlineData(1, new float[] { 0, 1 }, new float[] { 0, 1 })] + public void SetBlendTriangularShape_Success(float focus, float[] expectedFactors, float[] expectedPositions) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 0, true)) + { + brush.SetBlendTriangularShape(focus); + + Assert.Equal(expectedFactors, brush.Blend.Factors); + Assert.Equal(expectedPositions, brush.Blend.Positions); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 1, new float[] { 1, 0 }, new float[] { 0, 1 })] + [InlineData(0.5, 0, new float[] { 0, 0, 0 }, new float[] { 0, 0.5f, 1 })] + [InlineData(0.5, 1, new float[] { 0, 1, 0 }, new float[] { 0, 0.5f, 1 })] + [InlineData(1, 0.5, new float[] { 0, 0.5f }, new float[] { 0, 1 })] + public void SetBlendTriangularShape_Scale_Success(float focus, float scale, float[] expectedFactors, float[] expectedPositions) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 0, true)) + { + brush.SetBlendTriangularShape(focus, scale); + + Assert.Equal(expectedFactors, brush.Blend.Factors); + Assert.Equal(expectedPositions, brush.Blend.Positions); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-0.1)] + [InlineData(1.1)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void SetBlendTriangularShape_InvalidFocus_ThrowsArgumentException(float focus) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("focus", null, () => brush.SetBlendTriangularShape(focus)); + AssertExtensions.Throws("focus", null, () => brush.SetBlendTriangularShape(focus, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-0.1)] + [InlineData(1.1)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void SetBlendTriangularShape_InvalidScale_ThrowsArgumentException(float scale) + { + using (var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true)) + { + AssertExtensions.Throws("scale", null, () => brush.SetBlendTriangularShape(0.1f, scale)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetBlendTriangularShape_Disposed_ThrowsArgumentException() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.SetBlendTriangularShape(0)); + AssertExtensions.Throws(null, () => brush.SetBlendTriangularShape(0, 1)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Success() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + brush.Dispose(); + brush.Dispose(); + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.Core.cs b/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.Core.cs new file mode 100644 index 00000000000..5087847fa99 --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.Core.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Numerics; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public partial class MatrixTests + { + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MatrixElements_TestData))] + public void Ctor_Matrix3x2(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible) + { + Matrix3x2 matrix3X2 = new Matrix3x2(m11, m12, m21, m22, dx, dy); + using (var matrix = new Matrix(matrix3X2)) + { + Assert.Equal(new float[] { m11, m12, m21, m22, dx, dy }, matrix.Elements); + Assert.Equal(matrix3X2, matrix.MatrixElements); + Assert.Equal(isIdentity, matrix.IsIdentity); + Assert.Equal(isInvertible, matrix.IsInvertible); + Assert.Equal(dx, matrix.OffsetX); + Assert.Equal(dy, matrix.OffsetY); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MatrixElements_TestData))] + public void MatrixElements_RoundTrip(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible) + { + Matrix3x2 matrix3X2 = new Matrix3x2(m11, m12, m21, m22, dx, dy); + using (var matrix = new Matrix()) + { + matrix.MatrixElements = matrix3X2; + Assert.Equal(new float[] { m11, m12, m21, m22, dx, dy }, matrix.Elements); + Assert.Equal(matrix3X2, matrix.MatrixElements); + Assert.Equal(isIdentity, matrix.IsIdentity); + Assert.Equal(isInvertible, matrix.IsInvertible); + Assert.Equal(dx, matrix.OffsetX); + Assert.Equal(dy, matrix.OffsetY); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.cs b/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.cs new file mode 100644 index 00000000000..d5bc5e5424b --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/MatrixTests.cs @@ -0,0 +1,924 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public partial class MatrixTests + { + private static Matrix CreateDisposedMatrix() + { + var matrix = new Matrix(); + matrix.Dispose(); + return matrix; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + using (var matrix = new Matrix()) + { + Assert.Equal(new float[] { 1, 0, 0, 1, 0, 0 }, matrix.Elements); + Assert.True(matrix.IsIdentity); + Assert.True(matrix.IsInvertible); + Assert.Equal(0, matrix.OffsetX); + Assert.Equal(0, matrix.OffsetY); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(float.NaN)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + public void Ctor_FloatingPointBoundsInElements(float f) + { + Ctor_Elements(f, 0, 0, 1, 0, 0, false, false); + Ctor_Elements(1, f, 0, 1, 0, 0, false, false); + Ctor_Elements(1, 0, f, 1, 0, 0, false, false); + Ctor_Elements(1, 0, 0, f, 0, 0, false, false); + Ctor_Elements(1, 0, 0, 1, f, 0, false, false); + Ctor_Elements(1, 0, 0, 1, 0, f, false, false); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MatrixElements_TestData))] + public void Ctor_Elements(float m11, float m12, float m21, float m22, float dx, float dy, bool isIdentity, bool isInvertible) + { + using (var matrix = new Matrix(m11, m12, m21, m22, dx, dy)) + { + Assert.Equal(new float[] { m11, m12, m21, m22, dx, dy }, matrix.Elements); + Assert.Equal(isIdentity, matrix.IsIdentity); + Assert.Equal(isInvertible, matrix.IsInvertible); + Assert.Equal(dx, matrix.OffsetX); + Assert.Equal(dy, matrix.OffsetY); + } + } + + public static TheoryData MatrixElements_TestData + => new TheoryData + { + { 1, 0, 0, 1, 0, 0, true, true }, + { 0, 1, 2, 1, 3, 4, false, true }, + { 0, 0, 0, 0, 0, 0, false, false }, + { 1, 2, 3, 4, 5, 6, false, true }, + { -1, -2, -3, -4, -5, -6, false, true }, + { 123, 24, 82, 16, 47, 30, false, false }, + { 156, 46, 0, 0, 106, 19, false, false }, + { 146, 66, 158, 104, 42, 150, false, true }, + { 119, 140, 145, 74, 102, 58, false, true }, + { 1.1f, 0.1f, -0.1f, 0.9f, 0, 0, false, true }, + { 1.01f, 0.01f, -0.01f, 0.99f, 0, 0, false, true }, + { 1.001f, 0.001f, -0.001f, 0.999f, 0, 0, false, true }, + { 1.0001f, 0.0001f, -0.0001f, 0.9999f, 0, 0, true, true }, + { 1.0009f, 0.0009f, -0.0009f, 0.99995f, 0, 0, false, true } + }; + + public static IEnumerable Ctor_Rectangle_Points_TestData() + { + yield return new object[] { new Rectangle(1, 4, 8, 16), new Point[] { new Point(32, 64), new Point(128, 256), new Point(512, 1024) }, new float[] { 12, 24, 30, 60, -100, -200 }, false, false }; + yield return new object[] { new Rectangle(0, 0, 2, 4), new Point[] { new Point(8, 16), new Point(32, 64), new Point(128, 256) }, new float[] { 12, 24, 30, 60, 8, 16 }, false, false }; + yield return new object[] { new Rectangle(0, 0, 1, 1), new Point[] { new Point(0, 0), new Point(0, 0), new Point(0, 0) }, new float[] { 0, 0, 0, 0, 0, 0 }, false, false }; + yield return new object[] { new Rectangle(0, 0, 1, 1), new Point[] { new Point(0, 0), new Point(1, 0), new Point(0, 1) }, new float[] { 1, 0, 0, 1, 0, 0 }, true, true }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Points_TestData))] + public void Ctor_Rectangle_Points(Rectangle rect, Point[] plgpnts, float[] expectedElements, bool isIdentity, bool isInvertible) + { + using (var matrix = new Matrix(rect, plgpnts)) + { + Assert.Equal(expectedElements, matrix.Elements); + Assert.Equal(isIdentity, matrix.IsIdentity); + Assert.Equal(isInvertible, matrix.IsInvertible); + Assert.Equal(expectedElements[4], matrix.OffsetX); + Assert.Equal(expectedElements[5], matrix.OffsetY); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Rectangle_Points_TestData))] + public void Ctor_RectangleF_Points(Rectangle rect, Point[] plgpnts, float[] expectedElements, bool isIdentity, bool isInvertible) + { + using (var matrix = new Matrix(rect, plgpnts.Select(p => (PointF)p).ToArray())) + { + Assert.Equal(expectedElements, matrix.Elements); + Assert.Equal(isIdentity, matrix.IsIdentity); + Assert.Equal(isInvertible, matrix.IsInvertible); + Assert.Equal(expectedElements[4], matrix.OffsetX); + Assert.Equal(expectedElements[5], matrix.OffsetY); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullPoints_ThrowsArgumentNullException() + { + AssertExtensions.Throws("plgpts", () => new Matrix(new RectangleF(), null)); + AssertExtensions.Throws("plgpts", () => new Matrix(new Rectangle(), null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(2)] + [InlineData(4)] + public void Ctor_PointsLengthNotThree_ThrowsArgumentNullException(int length) + { + AssertExtensions.Throws(null, () => new Matrix(new RectangleF(), new PointF[length])); + AssertExtensions.Throws(null, () => new Matrix(new Rectangle(), new Point[length])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_WidthZero_ThrowsOutOfMemoryException() + { + Assert.Throws(() => new Matrix(new Rectangle(1, 1, 0, 1), new Point[3])); + Assert.Throws(() => new Matrix(new RectangleF(1, 1, 0, 1), new PointF[3])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_HeightZero_ThrowsOutOfMemoryException() + { + Assert.Throws(() => new Matrix(new Rectangle(1, 1, 1, 0), new Point[3])); + Assert.Throws(() => new Matrix(new RectangleF(1, 1, 1, 0), new PointF[3])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Matrix_ReturnsExpected() + { + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + using (Matrix clone = Assert.IsType(matrix.Clone())) + { + Assert.NotSame(matrix, clone); + Assert.Equal(new float[] { 1, 2, 3, 4, 5, 6 }, clone.Elements); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Clone()); + } + + public static IEnumerable Equals_TestData() + { + yield return new object[] { new Matrix(), new Matrix(1, 0, 0, 1, 0, 0), true }; + yield return new object[] { new Matrix(), new Matrix(123, 24, 82, 16, 47, 30), false }; + yield return new object[] { new Matrix(), new Matrix(1.1f, 0.1f, -0.1f, 0.9f, 0, 0), false }; + yield return new object[] { new Matrix(), new Matrix(1.01f, 0.01f, -0.01f, 0.99f, 0, 0), false }; + yield return new object[] { new Matrix(), new Matrix(1.001f, 0.001f, -0.001f, 0.999f, 0, 0), false }; + yield return new object[] { new Matrix(), new Matrix(1.0001f, 0.0001f, -0.0001f, 0.9999f, 0, 0), false }; + yield return new object[] { new Matrix(), new Matrix(1.0009f, 0.0009f, -0.0009f, 0.99995f, 0, 0), false }; + + var matrix = new Matrix(1, 2, 3, 4, 5, 6); + yield return new object[] { matrix, matrix, true }; + yield return new object[] { matrix.Clone(), matrix.Clone(), true }; + yield return new object[] { matrix.Clone(), new Matrix(1, 2, 3, 4, 5, 6), true }; + yield return new object[] { matrix.Clone(), new Matrix(2, 2, 3, 4, 5, 6), false }; + yield return new object[] { matrix.Clone(), new Matrix(1, 3, 3, 4, 5, 6), false }; + yield return new object[] { matrix.Clone(), new Matrix(1, 2, 4, 4, 5, 6), false }; + yield return new object[] { matrix.Clone(), new Matrix(1, 2, 3, 5, 5, 6), false }; + yield return new object[] { matrix.Clone(), new Matrix(1, 2, 3, 4, 6, 6), false }; + yield return new object[] { matrix.Clone(), new Matrix(1, 2, 3, 4, 5, 7), false }; + + yield return new object[] { new Matrix(), null, false }; + yield return new object[] { new Matrix(), new object(), false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Equals_TestData))] + public void Equals_Other_ReturnsExpected(Matrix matrix, object other, bool expected) + { + using (matrix) + using (other as IDisposable) + { + Assert.Equal(expected, matrix.Equals(other)); + if (other is Matrix otherMatrix) + { + Assert.Equal(ReferenceEquals(matrix, other), matrix.GetHashCode().Equals(other.GetHashCode())); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Equals(new Matrix())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_DisposedOther_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Matrix().Equals(CreateDisposedMatrix())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Elements_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Elements); + } + + public static IEnumerable Invert_TestData() + { + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), new float[] { -2, 1, 1.5f, -0.5f, 1, -2 } }; + yield return new object[] { new Matrix(1, 0, 0, 1, 8, 8), new float[] { 1, 0, 0, 1, -8, -8 } }; + yield return new object[] { new Matrix(), new float[] { 1, 0, 0, 1, 0, 0 } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Invert_TestData))] + public void Invert_Matrix_Success(Matrix matrix, float[] expectedElements) + { + using (matrix) + { + matrix.Invert(); + Assert.Equal(expectedElements, matrix.Elements); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(float.NaN)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void Invert_FloatBounds_ThrowsArgumentException(float f) + { + using (var matrix1 = new Matrix(f, 2, 3, 4, 5, 6)) + using (var matrix2 = new Matrix(1, f, 3, 4, 5, 6)) + using (var matrix3 = new Matrix(1, 2, f, 4, 5, 6)) + using (var matrix4 = new Matrix(1, 2, 3, f, 5, 6)) + using (var matrix5 = new Matrix(1, 2, 3, 4, f, 6)) + using (var matrix6 = new Matrix(1, 2, 3, 4, 5, f)) + { + AssertExtensions.Throws(null, () => matrix1.Invert()); + AssertExtensions.Throws(null, () => matrix2.Invert()); + AssertExtensions.Throws(null, () => matrix3.Invert()); + AssertExtensions.Throws(null, () => matrix4.Invert()); + AssertExtensions.Throws(null, () => matrix5.Invert()); + AssertExtensions.Throws(null, () => matrix6.Invert()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Invert_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Invert()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsIdentity_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().IsIdentity); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsInvertible_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().IsInvertible); + } + + public static IEnumerable Multiply_TestData() + { + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(10, 20, 30, 40, 50, 60), MatrixOrder.Prepend, new float[] { 700, 1000, 1500, 2200, 2350, 3460 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(10, 20, 30, 40, 50, 60), MatrixOrder.Append, new float[] { 700, 1000, 1500, 2200, 2350, 3460 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(), MatrixOrder.Prepend, new float[] { 10, 20, 30, 40, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(), MatrixOrder.Append, new float[] { 10, 20, 30, 40, 50, 60 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(0, 0, 0, 0, 0, 0), MatrixOrder.Prepend, new float[] { 0, 0, 0, 0, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(0, 0, 0, 0, 0, 0), MatrixOrder.Append, new float[] { 0, 0, 0, 0, 0, 0 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(1, 1, 1, 1, 1, 1), MatrixOrder.Prepend, new float[] { 40, 60, 40, 60, 90, 120 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(1, 1, 1, 1, 1, 1), MatrixOrder.Append, new float[] { 30, 30, 70, 70, 111, 111 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN), MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN), MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity), MatrixOrder.Prepend, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity), MatrixOrder.Append, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity), MatrixOrder.Prepend, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity), MatrixOrder.Append, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue), MatrixOrder.Prepend, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), new Matrix(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue), MatrixOrder.Append, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue } }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Multiply_TestData))] + public void Multiply_Matrix_Success(Matrix matrix, Matrix multiple, MatrixOrder order, float[] expected) + { + using (matrix) + using (multiple) + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + if (order == MatrixOrder.Prepend) + { + using (Matrix clone1 = matrix.Clone()) + { + clone1.Multiply(multiple); + Assert.Equal(expected, clone1.Elements); + } + } + matrix.Multiply(multiple, order); + Assert.Equal(expected, matrix.Elements); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Multiply_NullMatrix_ThrowsArgumentNullException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws("matrix", () => matrix.Multiply(null)); + AssertExtensions.Throws("matrix", () => matrix.Multiply(null, MatrixOrder.Prepend)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void Multiply_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + using (var other = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Multiply(other, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Multiply_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + using (var other = new Matrix()) + { + AssertExtensions.Throws(null, () => disposedMatrix.Multiply(other)); + AssertExtensions.Throws(null, () => disposedMatrix.Multiply(other, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Multiply_DisposedMatrix_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Multiply(disposedMatrix)); + AssertExtensions.Throws(null, () => matrix.Multiply(disposedMatrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Multiply_SameMatrix_ThrowsInvalidOperationException() + { + using (var matrix = new Matrix()) + { + Assert.Throws(() => matrix.Multiply(matrix)); + Assert.Throws(() => matrix.Multiply(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reset_Matrix_ReturnsExpected() + { + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + matrix.Reset(); + Assert.Equal(new float[] { 1, 0, 0, 1, 0, 0 }, matrix.Elements); + + matrix.Reset(); + Assert.Equal(new float[] { 1, 0, 0, 1, 0, 0 }, matrix.Elements); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Reset_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Reset()); + } + + public static IEnumerable Rotate_TestData() + { + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 180, PointF.Empty, MatrixOrder.Prepend, new float[] { -9.999996f, -19.9999943f, -30.0000019f, -40.0000038f, 50, 60 }, null, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 180, PointF.Empty, MatrixOrder.Append, new float[] { -9.999996f, -20, -30f, -40f, -50, -60 }, null, false }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 540, PointF.Empty, MatrixOrder.Prepend, new float[] { -9.999996f, -19.9999943f, -30.0000019f, -40.0000038f, 50, 60 }, null, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 540, PointF.Empty, MatrixOrder.Append, new float[] { -9.999996f, -20, -30f, -40f, -50, -60 }, null, false }; + + yield return new object[] { new Matrix(), 45, PointF.Empty, MatrixOrder.Prepend, new float[] { 0.707106769f, 0.707106769f, -0.707106829f, 0.707106769f, 0, 0 }, null, false }; + yield return new object[] { new Matrix(), 45, PointF.Empty, MatrixOrder.Append, new float[] { 0.707106769f, 0.707106769f, -0.707106829f, 0.707106769f, 0, 0 }, null, false }; + + using (var rotated45 = new Matrix()) + { + rotated45.Rotate(45); + yield return new object[] { rotated45.Clone(), 135, PointF.Empty, MatrixOrder.Prepend, new float[] { -1, 0, 0, -1, 0, 0 }, null, false }; + yield return new object[] { rotated45.Clone(), 135, PointF.Empty, MatrixOrder.Append, new float[] { -1, 0, 0, -1, 0, 0 }, null, false }; + } + + yield return new object[] { new Matrix(), 90, PointF.Empty, MatrixOrder.Prepend, new float[] { 0, 1, -1, 0, 0, 0 }, null, false }; + yield return new object[] { new Matrix(), 90, PointF.Empty, MatrixOrder.Append, new float[] { 0, 1, -1, 0, 0, 0 }, null, false }; + + using (var rotated90 = new Matrix()) + { + rotated90.Rotate(90); + yield return new object[] { rotated90.Clone(), 270, PointF.Empty, MatrixOrder.Prepend, new float[] { 1, 0, 0, 1, 0, 0 }, null, true }; + yield return new object[] { rotated90.Clone(), 270, PointF.Empty, MatrixOrder.Append, new float[] { 1, 0, 0, 1, 0, 0 }, null, true }; + } + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 180, new PointF(10, 10), MatrixOrder.Prepend, new float[] { -10, -20, -30, -40, 850, 1260 }, null, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 180, new PointF(10, 10), MatrixOrder.Append, new float[] { -10, -20, -30, -40, -30, -40 }, null, false }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, PointF.Empty, MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, 50, 60 }, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, PointF.Empty, MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, null, false }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, PointF.Empty, MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, 50, 60 }, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, PointF.Empty, MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, null, false }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, PointF.Empty, MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, 50, 60 }, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, false }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, PointF.Empty, MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN }, null, false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Rotate_TestData))] + public void Rotate_Matrix_Success(Matrix matrix, float angle, PointF point, MatrixOrder order, float[] expectedElements, float[] expectedElementsRotateAt, bool isIdentity) + { + using (matrix) + { + if (order == MatrixOrder.Prepend) + { + if (point == Point.Empty) + { + using (Matrix clone1 = matrix.Clone()) + { + clone1.Rotate(angle); + AssertEqualFloatArray(expectedElements, clone1.Elements); + Assert.Equal(isIdentity, clone1.IsIdentity); + } + } + + using (Matrix clone2 = matrix.Clone()) + { + clone2.RotateAt(angle, point); + AssertEqualFloatArray(expectedElementsRotateAt ?? expectedElements, clone2.Elements); + Assert.False(clone2.IsIdentity); + } + } + + if (point == Point.Empty) + { + using (Matrix clone3 = matrix.Clone()) + { + clone3.Rotate(angle, order); + AssertEqualFloatArray(expectedElements, clone3.Elements); + Assert.Equal(isIdentity, clone3.IsIdentity); + } + } + + using (Matrix clone4 = matrix.Clone()) + { + clone4.RotateAt(angle, point, order); + AssertEqualFloatArray(expectedElementsRotateAt ?? expectedElements, clone4.Elements); + Assert.False(clone4.IsIdentity); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Rotate_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedMatrix().Rotate(1, MatrixOrder.Append)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void Rotate_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Rotate(1, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateAt_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.RotateAt(1, PointF.Empty)); + AssertExtensions.Throws(null, () => disposedMatrix.RotateAt(1, PointF.Empty, MatrixOrder.Append)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void RotateAt_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.RotateAt(1, PointF.Empty, order)); + } + } + + public static IEnumerable Scale_TestData() + { + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 2, 4, MatrixOrder.Prepend, new float[] { 20, 40, 120, 160, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 2, 4, MatrixOrder.Append, new float[] { 20, 80, 60, 160, 100, 240 } }; + + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0.5, 0.25, MatrixOrder.Prepend, new float[] { 10, 20, 30, 40, 50, 60 } }; + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0.5, 0.25, MatrixOrder.Append, new float[] { 10, 10, 60, 40, 25, 15 } }; + + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0, 0, MatrixOrder.Prepend, new float[] { 0, 0, 0, 0, 50, 60 } }; + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0, 0, MatrixOrder.Append, new float[] { 0, 0, 0, 0, 0, 0 } }; + + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 1, 1, MatrixOrder.Prepend, new float[] { 20, 40, 120, 160, 50, 60 } }; + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 1, 1, MatrixOrder.Append, new float[] { 20, 40, 120, 160, 50, 60 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), -2, -4, MatrixOrder.Prepend, new float[] { -20, -40, -120, -160, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), -2, -4, MatrixOrder.Append, new float[] { -20, -80, -60, -160, -100, -240 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, float.NaN, MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, float.NaN, MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Prepend, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Append, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Prepend, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Append, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.MaxValue, float.MaxValue, MatrixOrder.Prepend, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.MaxValue, float.MaxValue, MatrixOrder.Append, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue } }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Scale_TestData))] + public void Scale_Matrix_Succss(Matrix matrix, float scaleX, float scaleY, MatrixOrder order, float[] expectedElements) + { + using (matrix) + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + if (order == MatrixOrder.Prepend) + { + using (Matrix clone = matrix.Clone()) + { + clone.Scale(scaleX, scaleY); + Assert.Equal(expectedElements, clone.Elements); + } + } + + matrix.Scale(scaleX, scaleY, order); + Assert.Equal(expectedElements, matrix.Elements); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void Scale_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Shear(1, 2, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Scale_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.Scale(1, 2)); + AssertExtensions.Throws(null, () => disposedMatrix.Scale(1, 2, MatrixOrder.Append)); + } + + public static IEnumerable Shear_TestData() + { + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 2, 4, MatrixOrder.Prepend, new float[] { 130, 180, 50, 80, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), 2, 4, MatrixOrder.Append, new float[] { 50, 60, 110, 160, 170, 260 } }; + + yield return new object[] { new Matrix(5, 3, 9, 2, 2, 1), 10, 20, MatrixOrder.Prepend, new float[] { 185, 43, 59, 32, 2, 1 } }; + yield return new object[] { new Matrix(5, 3, 9, 2, 2, 1), 10, 20, MatrixOrder.Append, new float[] { 35, 103, 29, 182, 12, 41 } }; + + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0, 0, MatrixOrder.Prepend, new float[] { 20, 40, 120, 160, 50, 60 } }; + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 0, 0, MatrixOrder.Append, new float[] { 20, 40, 120, 160, 50, 60 } }; + + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 1, 1, MatrixOrder.Prepend, new float[] { 140, 200, 140, 200, 50, 60 } }; + yield return new object[] { new Matrix(20, 40, 120, 160, 50, 60), 1, 1, MatrixOrder.Append, new float[] { 60, 60, 280, 280, 110, 110 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), -2, -4, MatrixOrder.Prepend, new float[] { -110, -140, 10, 0, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), -2, -4, MatrixOrder.Append, new float[] { -30, -20, -50, -80, -70, -140 } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, float.NaN, MatrixOrder.Prepend, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NaN, float.NaN, MatrixOrder.Append, new float[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Prepend, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Append, new float[] { float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Prepend, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Append, new float[] { float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity, float.NegativeInfinity } }; + + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.MaxValue, float.MaxValue, MatrixOrder.Prepend, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, 50, 60 } }; + yield return new object[] { new Matrix(10, 20, 30, 40, 50, 60), float.MaxValue, float.MaxValue, MatrixOrder.Append, new float[] { float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue } }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Shear_TestData))] + public void Shear_Matrix_Succss(Matrix matrix, float shearX, float shearY, MatrixOrder order, float[] expectedElements) + { + using (matrix) + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + if (order == MatrixOrder.Prepend) + { + using (Matrix clone = matrix.Clone()) + { + clone.Shear(shearX, shearY); + Assert.Equal(expectedElements, clone.Elements); + } + } + + matrix.Shear(shearX, shearY, order); + Assert.Equal(expectedElements, matrix.Elements); + } + } + + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void Shear_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Shear(1, 2, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Shear_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.Shear(1, 2)); + AssertExtensions.Throws(null, () => disposedMatrix.Shear(1, 2, MatrixOrder.Append)); + } + + public static IEnumerable Translate_TestData() + { + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), 5, 10, MatrixOrder.Prepend, new float[] { 2, 4, 6, 8, 80, 112 } }; + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), 5, 10, MatrixOrder.Append, new float[] { 2, 4, 6, 8, 15, 22 } }; + + yield return new object[] { new Matrix(), 5, 10, MatrixOrder.Prepend, new float[] { 1, 0, 0, 1, 5, 10 } }; + yield return new object[] { new Matrix(), 5, 10, MatrixOrder.Append, new float[] { 1, 0, 0, 1, 5, 10 } }; + + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.NaN, float.NaN, MatrixOrder.Prepend, new float[] { 1, 2, 3, 4, float.NaN, float.NaN } }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.NaN, float.NaN, MatrixOrder.Append, new float[] { 1, 2, 3, 4, float.NaN, float.NaN } }; + + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Prepend, new float[] { 1, 2, 3, 4, float.PositiveInfinity, float.PositiveInfinity } }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.PositiveInfinity, float.PositiveInfinity, MatrixOrder.Append, new float[] { 1, 2, 3, 4, float.PositiveInfinity, float.PositiveInfinity } }; + + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Prepend, new float[] { 1, 2, 3, 4, float.NegativeInfinity, float.NegativeInfinity } }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.NegativeInfinity, float.NegativeInfinity, MatrixOrder.Append, new float[] { 1, 2, 3, 4, float.NegativeInfinity, float.NegativeInfinity } }; + + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.MaxValue, float.MaxValue, MatrixOrder.Prepend, new float[] { 1, 2, 3, 4, float.MaxValue, float.MaxValue } }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), float.MaxValue, float.MaxValue, MatrixOrder.Append, new float[] { 1, 2, 3, 4, float.MaxValue, float.MaxValue } }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Translate_TestData))] + public void Translate_Matrix_Success(Matrix matrix, float offsetX, float offsetY, MatrixOrder order, float[] expectedElements) + { + using (matrix) + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + if (order == MatrixOrder.Prepend) + { + using (Matrix clone = matrix.Clone()) + { + clone.Translate(offsetX, offsetY); + AssertEqualFloatArray(expectedElements, clone.Elements); + } + } + + matrix.Translate(offsetX, offsetY, order); + AssertEqualFloatArray(expectedElements, matrix.Elements); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void Translate_InvalidMatrixOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.Translate(1, 2, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Translate_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.Translate(1, 2)); + AssertExtensions.Throws(null, () => disposedMatrix.Translate(1, 2, MatrixOrder.Append)); + } + + public static IEnumerable TransformPoints_TestData() + { + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), new Point[] { new Point(2, 4), new Point(4, 8) }, new Point[] { new Point(38, 52), new Point(66, 92) } }; + yield return new object[] { new Matrix(), new Point[] { new Point(2, 4), new Point(4, 8) }, new Point[] { new Point(2, 4), new Point(4, 8) } }; + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), new Point[1], new Point[] { new Point(10, 12) } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformPoints_TestData))] + public void TransformPoints_Point_Success(Matrix matrix, Point[] points, Point[] expectedPoints) + { + using (matrix) + { + matrix.TransformPoints(points); + Assert.Equal(expectedPoints, points); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformPoints_TestData))] + public void TransformPoints_PointF_Success(Matrix matrix, Point[] points, Point[] expectedPoints) + { + using (matrix) + { + PointF[] pointFs = points.Select(p => (PointF)p).ToArray(); + matrix.TransformPoints(pointFs); + Assert.Equal(expectedPoints.Select(p => (PointF)p), pointFs); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_NullPoints_ThrowsArgumentNullException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws("pts", () => matrix.TransformPoints((Point[])null)); + AssertExtensions.Throws("pts", () => matrix.TransformPoints((PointF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_EmptyPoints_ThrowsArgumentException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.TransformPoints(new Point[0])); + AssertExtensions.Throws(null, () => matrix.TransformPoints(new PointF[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.TransformPoints(new Point[1])); + AssertExtensions.Throws(null, () => disposedMatrix.TransformPoints(new PointF[1])); + } + + public static IEnumerable TransformVectors_TestData() + { + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), new Point[] { new Point(2, 4), new Point(4, 8) }, new Point[] { new Point(28, 40), new Point(56, 80) } }; + yield return new object[] { new Matrix(), new Point[] { new Point(2, 4), new Point(4, 8) }, new Point[] { new Point(2, 4), new Point(4, 8) } }; + yield return new object[] { new Matrix(2, 4, 6, 8, 10, 12), new Point[1], new Point[1] }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformVectors_TestData))] + public void TransformVectors_Point_Success(Matrix matrix, Point[] points, Point[] expectedPoints) + { + using (matrix) + { + matrix.TransformVectors(points); + Assert.Equal(expectedPoints, points); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformVectors_TestData))] + public void TransformVectors_PointF_Success(Matrix matrix, Point[] points, Point[] expectedPoints) + { + using (matrix) + { + PointF[] pointFs = points.Select(p => (PointF)p).ToArray(); + matrix.TransformVectors(pointFs); + Assert.Equal(expectedPoints.Select(p => (PointF)p), pointFs); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformVectors_TestData))] + public void VectorTransformPoints_Points_Success(Matrix matrix, Point[] points, Point[] expectedPoints) + { + using (matrix) + { + matrix.VectorTransformPoints(points); + Assert.Equal(expectedPoints, points); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformVectors_NullPoints_ThrowsArgumentNullException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws("pts", () => matrix.VectorTransformPoints(null)); + AssertExtensions.Throws("pts", () => matrix.TransformVectors((Point[])null)); + AssertExtensions.Throws("pts", () => matrix.TransformVectors((PointF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformVectors_EmptyPoints_ThrowsArgumentException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => matrix.VectorTransformPoints(new Point[0])); + AssertExtensions.Throws(null, () => matrix.TransformVectors(new Point[0])); + AssertExtensions.Throws(null, () => matrix.TransformVectors(new PointF[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformVectors_Disposed_ThrowsArgumentException() + { + Matrix disposedMatrix = CreateDisposedMatrix(); + + AssertExtensions.Throws(null, () => disposedMatrix.VectorTransformPoints(new Point[1])); + AssertExtensions.Throws(null, () => disposedMatrix.TransformPoints(new Point[1])); + AssertExtensions.Throws(null, () => disposedMatrix.TransformVectors(new PointF[1])); + } + + private static void AssertEqualFloatArray(float[] expected, float[] actual) + { + Assert.Equal(expected.Length, actual.Length); + for (int i = 0; i < expected.Length; i++) + { + try + { + Assert.Equal((double)expected[i], (double)actual[i], 3); + } + catch + { + Console.WriteLine(i); + Console.WriteLine($"Expected: {string.Join(", ", expected)}"); + Console.WriteLine($"Actual: {string.Join(", ", actual)}"); + throw; + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Drawing2D/PathGradientBrushTests.cs b/src/System.Drawing.Common/tests/Drawing2D/PathGradientBrushTests.cs new file mode 100644 index 00000000000..8a80601c43b --- /dev/null +++ b/src/System.Drawing.Common/tests/Drawing2D/PathGradientBrushTests.cs @@ -0,0 +1,1075 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Xunit; + +namespace System.Drawing.Drawing2D.Tests +{ + public class PathGradientBrushTests + { + private readonly Point[] _defaultIntPoints = new Point[2] { new Point(1, 2), new Point(20, 30) }; + private readonly PointF[] _defaultFloatPoints = new PointF[2] { new PointF(1, 2), new PointF(20, 30) }; + private readonly RectangleF _defaultRectangle = new RectangleF(1, 2, 19, 28); + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Ctor_Points_ReturnsExpected() + { + using (PathGradientBrush bi = new PathGradientBrush(_defaultIntPoints)) + using (PathGradientBrush bf = new PathGradientBrush(_defaultFloatPoints)) + { + AssertDefaults(bi); + Assert.Equal(WrapMode.Clamp, bi.WrapMode); + AssertDefaults(bf); + Assert.Equal(WrapMode.Clamp, bf.WrapMode); + } + } + + public static IEnumerable WrapMode_TestData() + { + yield return new object[] { WrapMode.Clamp }; + yield return new object[] { WrapMode.Tile }; + yield return new object[] { WrapMode.TileFlipX }; + yield return new object[] { WrapMode.TileFlipXY }; + yield return new object[] { WrapMode.TileFlipY }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(WrapMode_TestData))] + public void Ctor_PointsWrapMode_ReturnsExpected(WrapMode wrapMode) + { + using (PathGradientBrush brushInt = new PathGradientBrush(_defaultIntPoints, wrapMode)) + using (PathGradientBrush brushFloat = new PathGradientBrush(_defaultFloatPoints, wrapMode)) + { + AssertDefaults(brushInt); + Assert.Equal(wrapMode, brushInt.WrapMode); + AssertDefaults(brushFloat); + Assert.Equal(wrapMode, brushFloat.WrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PointsNull_ThrowsArgumentNullException() + { + AssertExtensions.Throws("points", () => new PathGradientBrush((Point[])null)); + AssertExtensions.Throws("points", () => new PathGradientBrush((PointF[])null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void Ctor_PointsLengthLessThenTwo_ThrowsOutOfMemoryException(int pointsLength) + { + Assert.Throws(() => new PathGradientBrush(new Point[pointsLength])); + Assert.Throws(() => new PathGradientBrush(new Point[pointsLength], WrapMode.Clamp)); + Assert.Throws(() => new PathGradientBrush(new PointF[pointsLength])); + Assert.Throws(() => new PathGradientBrush(new PointF[pointsLength], WrapMode.Clamp)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_InvalidWrapMode_ThrowsInvalidEnumArgumentException() + { + Assert.ThrowsAny(() => + new PathGradientBrush(_defaultIntPoints, (WrapMode)int.MaxValue)); + + Assert.ThrowsAny(() => + new PathGradientBrush(_defaultFloatPoints, (WrapMode)int.MaxValue)); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Ctor_Path_ReturnsExpected() + { + using (GraphicsPath path = new GraphicsPath(_defaultFloatPoints, new byte[] { 0, 1 })) + using (PathGradientBrush brush = new PathGradientBrush(path)) + { + AssertDefaults(brush); + Assert.Equal(WrapMode.Clamp, brush.WrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Path_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => new PathGradientBrush((GraphicsPath)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PathWithLessThenTwoPoints_ThrowsOutOfMemoryException() + { + using (GraphicsPath path = new GraphicsPath()) + { + Assert.Throws(() => new PathGradientBrush(path)); + path.AddLines(new PointF[] { new PointF(1, 1) }); + Assert.Throws(() => new PathGradientBrush(path)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Clone_ReturnsExpected() + { + using (GraphicsPath path = new GraphicsPath(_defaultFloatPoints, new byte[] { 0, 1 })) + using (PathGradientBrush brush = new PathGradientBrush(path)) + using (PathGradientBrush clone = Assert.IsType(brush.Clone())) + { + AssertDefaults(clone); + Assert.Equal(WrapMode.Clamp, clone.WrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Clone()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CenterColor_ReturnsExpected() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.Equal(Color.Black.ToArgb(), brush.CenterColor.ToArgb()); + brush.CenterColor = Color.Blue; + Assert.Equal(Color.Blue.ToArgb(), brush.CenterColor.ToArgb()); + brush.CenterColor = Color.Transparent; + Assert.Equal(Color.Transparent.ToArgb(), brush.CenterColor.ToArgb()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CenterColor_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.CenterColor = Color.Blue); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void SurroundColors_ReturnsExpected() + { + Color[] expectedColors = new Color[2] { Color.FromArgb(255, 0, 0, 255), Color.FromArgb(255, 255, 0, 0) }; + Color[] sameColors = new Color[2] { Color.FromArgb(255, 255, 255, 0), Color.FromArgb(255, 255, 255, 0) }; + Color[] expectedSameColors = new Color[1] { Color.FromArgb(255, 255, 255, 0) }; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SurroundColors = expectedColors; + Assert.Equal(expectedColors, brush.SurroundColors); + brush.SurroundColors = sameColors; + Assert.Equal(expectedSameColors, brush.SurroundColors); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SurroundColors_CannotChange() + { + Color[] colors = new Color[2] { Color.FromArgb(255, 0, 0, 255), Color.FromArgb(255, 255, 0, 0) }; + Color[] defaultColors = new Color[1] { Color.FromArgb(255, 255, 255, 255) }; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SurroundColors.ToList().AddRange(colors); + Assert.Equal(defaultColors, brush.SurroundColors); + brush.SurroundColors[0] = Color.FromArgb(255, 0, 0, 255); + Assert.NotEqual(Color.FromArgb(255, 0, 0, 255), brush.SurroundColors[0]); + Assert.Equal(defaultColors, brush.SurroundColors); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SurroundColors_Disposed_ThrowsArgumentException() + { + Color[] colors = new Color[2] { Color.FromArgb(255, 0, 0, 255), Color.FromArgb(255, 255, 0, 0) }; + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.SurroundColors = colors); + } + + public static IEnumerable SurroundColors_InvalidColorsLength_TestData() + { + yield return new object[] { new Point[2] { new Point(1, 1), new Point(2, 2) }, new Color[0] }; + yield return new object[] { new Point[2] { new Point(1, 1), new Point(2, 2) }, new Color[3] }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SurroundColors_InvalidColorsLength_TestData))] + public void SurroundColors_InvalidColorsLength_ThrowsArgumentException(Point[] points, Color[] colors) + { + using (PathGradientBrush brush = new PathGradientBrush(points)) + { + AssertExtensions.Throws(null, () => brush.SurroundColors = colors); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SurroundColors_Null_ThrowsNullReferenceException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(() => brush.SurroundColors = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CenterPoint_ReturnsExpected() + { + PointF centralPoint = new PointF(float.MaxValue, float.MinValue); + PointF defaultCentralPoint = new PointF(10.5f, 16f); + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints, WrapMode.TileFlipXY)) + { + Assert.Equal(defaultCentralPoint, brush.CenterPoint); + brush.CenterPoint = centralPoint; + Assert.Equal(centralPoint, brush.CenterPoint); + + centralPoint.X = float.NaN; + centralPoint.Y = float.NegativeInfinity; + brush.CenterPoint = centralPoint; + Assert.Equal(float.NaN, brush.CenterPoint.X); + Assert.Equal(float.NegativeInfinity, brush.CenterPoint.Y); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CenterPoint_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.CenterPoint); + } + + public static IEnumerable Blend_FactorsPositions_TestData() + { + yield return new object[] { new float[1] { 1 }, new float[1] { 0 } }; + yield return new object[] { new float[2] { 1, 1 }, new float[2] { 0, 1 } }; + yield return new object[] { new float[3] { 1, 0, 1 }, new float[3] { 0, 3, 1 } }; + yield return new object[] { new float[1] { 1 }, new float[3] { 0, 3, 1 } }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Blend_FactorsPositions_TestData))] + public void Blend_ReturnsExpected(float[] factors, float[] positions) + { + int expectedSize = factors.Length; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints, WrapMode.TileFlipXY)) + { + brush.Blend = new Blend { Factors = factors, Positions = positions }; + Assert.Equal(factors, brush.Blend.Factors); + Assert.Equal(expectedSize, brush.Blend.Positions.Length); + if (expectedSize == positions.Length && expectedSize != 1) + { + Assert.Equal(factors, brush.Blend.Factors); + Assert.Equal(positions, brush.Blend.Positions); + } + else + { + Assert.Equal(factors, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_CannotChange() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints, WrapMode.TileFlipXY)) + { + brush.Blend.Factors = new float[0]; + Assert.Equal(1, brush.Blend.Factors.Length); + brush.Blend.Factors = new float[2]; + Assert.Equal(1, brush.Blend.Factors.Length); + brush.Blend.Positions = new float[0]; + Assert.Equal(1, brush.Blend.Positions.Length); + brush.Blend.Positions = new float[2]; + Assert.Equal(1, brush.Blend.Positions.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Blend); + } + + public static IEnumerable Blend_InvalidFactorsPositions_TestData() + { + yield return new object[] { new Blend() { Factors = new float[0], Positions = new float[0] } }; + yield return new object[] { new Blend() { Factors = new float[2], Positions = new float[2] { 1, 1 } } }; + yield return new object[] { new Blend() { Factors = new float[2], Positions = new float[2] { 0, 5 } } }; + yield return new object[] { new Blend() { Factors = new float[3], Positions = new float[3] { 0, 1, 5 } } }; + yield return new object[] { new Blend() { Factors = new float[3], Positions = new float[3] { 1, 1, 1 } } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Blend_InvalidFactorsPositions_TestData))] + public void Blend_InvalidFactorPositions_ThrowsArgumentException(Blend blend) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => brush.Blend = blend); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_InvalidFactorPositionsLengthMismatch_ThrowsArgumentOutOfRangeException() + { + Blend invalidBlend = new Blend() { Factors = new float[2], Positions = new float[1] }; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("value", null, () => brush.Blend = invalidBlend); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_Null_ThrowsNullReferenceException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.Throws(() => brush.Blend = null); + Assert.Throws(() => brush.Blend = new Blend() { Factors = null, Positions = null}); + Assert.Throws(() => brush.Blend = new Blend() { Factors = null, Positions = new float[0] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Blend_NullBlendProperites_ThrowsArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("value", "source", () => + brush.Blend = new Blend() { Factors = new float[0], Positions = null }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1f)] + [InlineData(0f)] + [InlineData(0.5f)] + public void SetSigmaBellShape_Focus_Success(float focus) + { + float defaultScale = 1f; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SetSigmaBellShape(focus); + Assert.True(brush.Transform.IsIdentity); + if (focus == 0f) + { + Assert.Equal(focus, brush.Blend.Positions[0]); + Assert.Equal(defaultScale, brush.Blend.Factors[0]); + Assert.Equal(1f, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(0f, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + else if (focus == 1f) + { + Assert.Equal(0f, brush.Blend.Positions[0]); + Assert.Equal(0f, brush.Blend.Factors[0]); + Assert.Equal(focus, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(defaultScale, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + else + { + Assert.Equal(0f, brush.Blend.Positions[0]); + Assert.Equal(0f, brush.Blend.Factors[0]); + Assert.Equal(1f, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(0f, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1f)] + [InlineData(0f)] + [InlineData(0.5f)] + public void SetSigmaBellShape_FocusScale_Success(float focus) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SetSigmaBellShape(focus); + Assert.True(brush.Transform.IsIdentity); + if (focus == 0f) + { + Assert.Equal(256, brush.Blend.Positions.Length); + Assert.Equal(256, brush.Blend.Factors.Length); + Assert.Equal(focus, brush.Blend.Positions[0]); + Assert.Equal(1f, brush.Blend.Factors[0]); + Assert.Equal(1f, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(0f, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + else if (focus == 1f) + { + Assert.Equal(256, brush.Blend.Positions.Length); + Assert.Equal(256, brush.Blend.Factors.Length); + Assert.Equal(0f, brush.Blend.Positions[0]); + Assert.Equal(0f, brush.Blend.Factors[0]); + Assert.Equal(focus, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(1f, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + else + { + Assert.Equal(511, brush.Blend.Positions.Length); + Assert.Equal(511, brush.Blend.Factors.Length); + Assert.Equal(0f, brush.Blend.Positions[0]); + Assert.Equal(0f, brush.Blend.Factors[0]); + Assert.Equal(focus, brush.Blend.Positions[255]); + Assert.Equal(1f, brush.Blend.Factors[255]); + Assert.Equal(1f, brush.Blend.Positions[brush.Blend.Positions.Length - 1]); + Assert.Equal(0f, brush.Blend.Factors[brush.Blend.Factors.Length - 1]); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetSigmaBellShape_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.SetSigmaBellShape(1f)); + AssertExtensions.Throws(null, () => brush.SetSigmaBellShape(1f, 1f)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(1.1f)] + public void SetSigmaBellShape_InvalidFocus_ThrowsArgumentException(float focus) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("focus", null, () => brush.SetSigmaBellShape(focus)); + AssertExtensions.Throws("focus", null, () => brush.SetSigmaBellShape(focus, 1f)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(1.1f)] + public void SetSigmaBellShape_InvalidScale_ThrowsArgumentException(float scale) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("scale", null, () => brush.SetSigmaBellShape(1f, scale)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1f)] + [InlineData(0f)] + [InlineData(0.5f)] + public void SetBlendTriangularShape_Focus_Success(float focus) + { + float defaultScale = 1f; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SetBlendTriangularShape(focus); + Assert.True(brush.Transform.IsIdentity); + if (focus == 0f) + { + Assert.Equal(new float[2] { defaultScale, 0f }, brush.Blend.Factors); + Assert.Equal(new float[2] { focus, 1f }, brush.Blend.Positions); + } + else if (focus == 1f) + { + Assert.Equal(new float[2] { 0f, defaultScale }, brush.Blend.Factors); + Assert.Equal(new float[2] { 0f, focus }, brush.Blend.Positions); + } + else + { + Assert.Equal(new float[3] { 0f, defaultScale, 0f }, brush.Blend.Factors); + Assert.Equal(new float[3] { 0f, focus, 1f }, brush.Blend.Positions); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1f)] + [InlineData(0f)] + [InlineData(0.5f)] + public void SetBlendTriangularShape_FocusScale_Success(float focus) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.SetBlendTriangularShape(focus); + Assert.True(brush.Transform.IsIdentity); + Assert.True(brush.Transform.IsIdentity); + if (focus == 0f) + { + Assert.Equal(new float[2] { 1f, 0f }, brush.Blend.Factors); + Assert.Equal(new float[2] { focus, 1f }, brush.Blend.Positions); + } + else if (focus == 1f) + { + Assert.Equal(new float[2] { 0f, 1f }, brush.Blend.Factors); + Assert.Equal(new float[2] { 0f, focus }, brush.Blend.Positions); + } + else + { + Assert.Equal(new float[3] { 0f, 1f, 0f }, brush.Blend.Factors); + Assert.Equal(new float[3] { 0f, focus, 1f }, brush.Blend.Positions); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetBlendTriangularShape_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.SetBlendTriangularShape(1f)); + AssertExtensions.Throws(null, () => brush.SetBlendTriangularShape(1f, 1f)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(1.1f)] + public void SetBlendTriangularShape_InvalidFocus_ThrowsArgumentException(float focus) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("focus", null, () => brush.SetBlendTriangularShape(focus)); + AssertExtensions.Throws("focus", null, () => brush.SetBlendTriangularShape(focus, 1f)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(1.1f)] + public void SetBlendTriangularShape_InvalidScale_ThrowsArgumentException(float scale) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("scale", null, () => brush.SetBlendTriangularShape(1f, scale)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_ReturnsExpected() + { + Color[] expectedColors = new Color[2] { Color.FromArgb(255, 0, 0, 255), Color.FromArgb(255, 255, 0, 0) }; + float[] expectedPositions = new float[] { 0, 1 }; + Color[] sameColors = new Color[2] { Color.FromArgb(255, 255, 255, 0), Color.FromArgb(255, 255, 255, 0) }; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.InterpolationColors = new ColorBlend() { Colors = expectedColors, Positions = expectedPositions }; + Assert.Equal(expectedColors, brush.InterpolationColors.Colors); + Assert.Equal(expectedPositions, brush.InterpolationColors.Positions); + + brush.InterpolationColors = new ColorBlend() { Colors = sameColors, Positions = expectedPositions }; + Assert.Equal(sameColors, brush.InterpolationColors.Colors); + Assert.Equal(expectedPositions, brush.InterpolationColors.Positions); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void InterpolationColors_CannotChange() + { + Color[] colors = new Color[2] { Color.FromArgb(255, 0, 0, 255), Color.FromArgb(255, 255, 0, 0) }; + Color[] defaultColors = new Color[1] { Color.Empty }; + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.InterpolationColors.Colors.ToList().AddRange(colors); + Assert.Equal(defaultColors, brush.InterpolationColors.Colors); + brush.InterpolationColors.Colors = colors; + Assert.Equal(defaultColors, brush.InterpolationColors.Colors); + brush.InterpolationColors.Colors[0] = Color.Pink; + Assert.NotEqual(Color.Pink, brush.InterpolationColors.Colors[0]); + Assert.Equal(defaultColors, brush.InterpolationColors.Colors); + brush.InterpolationColors.Positions = new float[0]; + Assert.Equal(1, brush.InterpolationColors.Positions.Length); + brush.InterpolationColors.Positions = new float[2]; + Assert.Equal(1, brush.InterpolationColors.Positions.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.InterpolationColors); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_Null_ThrowsNullReferenceException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.Throws(() => brush.InterpolationColors = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_NullColors_ThrowsNullReferenceException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.Throws(() => + brush.InterpolationColors = new ColorBlend() { Colors = null, Positions = null }); + + Assert.Throws(() => + brush.InterpolationColors = new ColorBlend() { Colors = null, Positions = new float[2] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_NullPoints_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("value", "source", () => + brush.InterpolationColors = new ColorBlend() { Colors = new Color[1], Positions = null }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_Empty_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => brush.InterpolationColors = new ColorBlend()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_EmptyColors_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => + brush.InterpolationColors = new ColorBlend() { Colors = new Color[0], Positions = new float[0] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_PointsLengthGreaterThenColorsLength_ArgumentOutOfRangeException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("value", null, () => + brush.InterpolationColors = new ColorBlend() { Colors = new Color[1], Positions = new float[2] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationColors_ColorsLengthGreaterThenPointsLength_ThrowsArgumentOutOfRangeException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.Throws(() => + brush.InterpolationColors = new ColorBlend() { Colors = new Color[2], Positions = new float[1] }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_ReturnsExpected() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix defaultMatrix = new Matrix(1, 0, 0, 1, 0, 0)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 1, 1)) + { + Assert.Equal(defaultMatrix, brush.Transform); + brush.Transform = matrix; + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_EmptyMatrix_ReturnsExpected() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix()) + { + brush.Transform = matrix; + Assert.True(brush.Transform.IsIdentity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Transform); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_Null_ArgumentNullException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("value", "matrix", () => brush.Transform = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_NonInvertible_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix nonInvertible = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => brush.Transform = nonInvertible); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Success() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix defaultMatrix = new Matrix(1, 0, 0, 1, 0, 0)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 1, 1)) + { + Assert.Equal(defaultMatrix, brush.Transform); + brush.Transform = matrix; + Assert.Equal(matrix, brush.Transform); + brush.ResetTransform(); + Assert.Equal(defaultMatrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ResetTransform()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Matrix_Success() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix defaultMatrix = new Matrix(1, 0, 0, 1, 0, 0)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 1, 1)) + { + defaultMatrix.Multiply(matrix, MatrixOrder.Prepend); + brush.MultiplyTransform(matrix); + Assert.Equal(defaultMatrix, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Append)] + [InlineData(MatrixOrder.Prepend)] + public void MultiplyTransform_MatrixMatrixOrder_Success(MatrixOrder matrixOrder) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix defaultMatrix = new Matrix(1, 0, 0, 1, 0, 0)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 1, 1)) + { + defaultMatrix.Multiply(matrix, matrixOrder); + brush.MultiplyTransform(matrix, matrixOrder); + Assert.Equal(defaultMatrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Disposed_ThrowsArgumentException() + { + using (Matrix matrix = new Matrix(1, 0, 0, 1, 1, 1)) + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NullMatrix_ThrowsArgumentNullException() + { + using (var brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null)); + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_DisposedMatrix_Nop() + { + using (var brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix transform = brush.Transform) + { + var matrix = new Matrix(); + matrix.Dispose(); + + brush.MultiplyTransform(matrix); + brush.MultiplyTransform(matrix, MatrixOrder.Append); + + Assert.Equal(transform, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_InvalidMatrixOrder_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 1, 1, 1, 1, 1)) + { + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix, (MatrixOrder)int.MinValue)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NonInvertible_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix nonInvertible = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => brush.MultiplyTransform(nonInvertible)); + AssertExtensions.Throws(null, () => brush.MultiplyTransform(nonInvertible, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Offset_Success() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Translate(20f, 30f, MatrixOrder.Prepend); + brush.TranslateTransform(20f, 30f); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Append)] + [InlineData(MatrixOrder.Prepend)] + public void TranslateTransform_OffsetMatrixOrder_Success(MatrixOrder matrixOrder) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Translate(20f, 30f, matrixOrder); + brush.TranslateTransform(20f, 30f, matrixOrder); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.TranslateTransform(20f, 30f, MatrixOrder.Append)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_InvalidMatrixOrder_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => brush.TranslateTransform(20f, 30f, (MatrixOrder)int.MinValue)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Scale_Success() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Scale(2, 4, MatrixOrder.Prepend); + brush.ScaleTransform(2, 4); + Assert.Equal(matrix, brush.Transform); + + matrix.Scale(0.5f, 0.25f, MatrixOrder.Prepend); + brush.ScaleTransform(0.5f, 0.25f); + Assert.True(brush.Transform.IsIdentity); + + matrix.Scale(float.MaxValue, float.MinValue, MatrixOrder.Prepend); + brush.ScaleTransform(float.MaxValue, float.MinValue); + Assert.Equal(matrix, brush.Transform); + + matrix.Scale(float.MinValue, float.MaxValue, MatrixOrder.Prepend); + brush.ScaleTransform(float.MinValue, float.MaxValue); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Append)] + [InlineData(MatrixOrder.Prepend)] + public void ScaleTransform_ScaleMatrixOrder_Success(MatrixOrder matrixOrder) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Scale(0.25f, 2, matrixOrder); + brush.ScaleTransform(0.25f, 2, matrixOrder); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ScaleTransform(0.25f, 2, MatrixOrder.Append)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_InvalidMatrixOrder_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => brush.ScaleTransform(1, 1, (MatrixOrder)int.MinValue)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Angle_Success() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Rotate(90, MatrixOrder.Prepend); + brush.RotateTransform(90); + Assert.Equal(matrix, brush.Transform); + + brush.RotateTransform(270); + Assert.True(brush.Transform.IsIdentity); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Append)] + [InlineData(MatrixOrder.Prepend)] + public void RotateTransform_AngleMatrixOrder_Success(MatrixOrder matrixOrder) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + using (Matrix matrix = new Matrix(1, 0, 0, 1, 0, 0)) + { + matrix.Rotate(45, matrixOrder); + brush.RotateTransform(45, matrixOrder); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.RotateTransform(45, MatrixOrder.Append)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_InvalidMatrixOrder_ArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + AssertExtensions.Throws(null, () => brush.RotateTransform(45, (MatrixOrder)int.MinValue)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FocusScales_ReturnsExpected() + { + var point = new PointF(2.5f, 3.4f); + + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.FocusScales = point; + Assert.Equal(point, brush.FocusScales); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FocusScales_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.FocusScales); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(WrapMode_TestData))] + public void WrapMode_ReturnsExpected(WrapMode wrapMode) + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + brush.WrapMode = wrapMode; + Assert.Equal(wrapMode, brush.WrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_Disposed_ThrowsArgumentException() + { + PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.WrapMode); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_Invalid_InvalidEnumArgumentException() + { + using (PathGradientBrush brush = new PathGradientBrush(_defaultFloatPoints)) + { + Assert.ThrowsAny(() => brush.WrapMode = (WrapMode)int.MinValue); + } + } + + private void AssertDefaults(PathGradientBrush brush) + { + Assert.Equal(_defaultRectangle, brush.Rectangle); + Assert.Equal(new float[] { 1 }, brush.Blend.Factors); + Assert.Equal(1, brush.Blend.Positions.Length); + Assert.Equal(new PointF(10.5f, 16f), brush.CenterPoint); + Assert.Equal(new Color[] { Color.Empty }, brush.InterpolationColors.Colors); + Assert.Equal(new Color[] { Color.FromArgb(255, 255, 255, 255) }, brush.SurroundColors); + Assert.Equal(new float[] { 0 }, brush.InterpolationColors.Positions); + Assert.True(brush.Transform.IsIdentity); + Assert.True(brush.FocusScales.IsEmpty); + } + } +} diff --git a/src/System.Drawing.Common/tests/DrawingTest.cs b/src/System.Drawing.Common/tests/DrawingTest.cs new file mode 100644 index 00000000000..10b76e34800 --- /dev/null +++ b/src/System.Drawing.Common/tests/DrawingTest.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace System.Drawing.Tests +{ + public abstract class DrawingTest + { + private static Security.Cryptography.MD5 s_md5 = Security.Cryptography.MD5.Create(); + + protected void ValidateImageContent(Image image, byte[] expectedHash) + { + using (MemoryStream stream = new MemoryStream(4096)) + { + image.Save(stream, ImageFormat.Bmp); + stream.Seek(0, SeekOrigin.Begin); + byte[] hash = s_md5.ComputeHash(stream); + Assert.Equal(expectedHash, hash); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/FontFamilyTests.cs b/src/System.Drawing.Common/tests/FontFamilyTests.cs new file mode 100644 index 00000000000..c3790bf806b --- /dev/null +++ b/src/System.Drawing.Common/tests/FontFamilyTests.cs @@ -0,0 +1,300 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Text; +using Xunit; + +namespace System.Drawing.Tests +{ + public class FontFamilyTests + { + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GenericFontFamilies.Serif - 1, "Courier New")] // Value is outside the enum range. + [InlineData(GenericFontFamilies.Monospace + 1, "Courier New")] // Value is outside the enum range. + [InlineData(GenericFontFamilies.Monospace, "Courier New")] + [InlineData(GenericFontFamilies.SansSerif, "Microsoft Sans Serif")] + [InlineData(GenericFontFamilies.Serif, "Times New Roman")] + public void Ctor_GenericFamily(GenericFontFamilies genericFamily, string expectedName) + { + using (var fontFamily = new FontFamily(genericFamily)) + { + Assert.Equal(expectedName, fontFamily.Name); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("Courier New", "Courier New")] + [InlineData("Microsoft Sans Serif", "Microsoft Sans Serif")] + [InlineData("Times New Roman", "Times New Roman")] + [InlineData("times new roman", "Times New Roman")] + public void Ctor_Name(string name, string expectedName) + { + using (var fontFamily = new FontFamily(name)) + { + Assert.Equal(expectedName, fontFamily.Name); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Name_FontCollection() + { + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + using (var fontFamily = new FontFamily("Code New Roman", fontCollection)) + { + Assert.Equal("Code New Roman", fontFamily.Name); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(null)] + [InlineData("NoSuchFont")] + [InlineData("Serif")] + public void Ctor_NoSuchFontName_ThrowsArgumentException(string name) + { + AssertExtensions.Throws(null, () => new FontFamily(name)); + AssertExtensions.Throws(null, () => new FontFamily(name, null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NoSuchFontNameInCollection_ThrowsArgumentException() + { + using (var fontCollection = new PrivateFontCollection()) + { + AssertExtensions.Throws(null, () => new FontFamily("Times New Roman", fontCollection)); + } + } + + public static IEnumerable Equals_TestData() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + yield return new object[] { fontFamily, fontFamily, true }; + yield return new object[] { FontFamily.GenericMonospace, FontFamily.GenericMonospace, true }; + yield return new object[] { FontFamily.GenericMonospace, FontFamily.GenericSansSerif, false }; + + yield return new object[] { FontFamily.GenericSansSerif, new object(), false }; + yield return new object[] { FontFamily.GenericSansSerif, null, false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Equals_TestData))] + public void Equals_Object_ReturnsExpected(FontFamily fontFamily, object other, bool expected) + { + try + { + Assert.Equal(expected, fontFamily.Equals(other)); + } + finally + { + fontFamily.Dispose(); + (other as IDisposable)?.Dispose(); + } + } + + // This will fail on any platform we use libgdiplus, with any + // installed system fonts whose name is longer than 31 chars. + // macOS 10.15+ ships out of the box with a problem font + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Families_Get_ReturnsExpected() + { +#pragma warning disable 0618 // FontFamily.GetFamilies is deprecated. + using (var image = new Bitmap(10, 10)) + using (var graphics = Graphics.FromImage(image)) + { + FontFamily[] families = FontFamily.Families; + FontFamily[] familiesWithGraphics = FontFamily.GetFamilies(graphics); + + // FontFamily.Equals uses the native handle to determine equality. However, GDI+ does not always + // cache handles, so we cannot just Assert.Equal(families, familiesWithGraphics); + Assert.Equal(families.Length, familiesWithGraphics.Length); + + for (int i = 0; i < families.Length; i++) + { + Assert.Equal(families[i].Name, familiesWithGraphics[i].Name); + } + + foreach (FontFamily fontFamily in families) + { + using (FontFamily copy = new FontFamily(fontFamily.Name)) + { + Assert.Equal(fontFamily.Name, copy.Name); + } + } + } +#pragma warning restore 0618 + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GenericMonospace_Get_ReturnsExpected() + { + using (FontFamily fontFamily1 = FontFamily.GenericMonospace) + { + using (FontFamily fontFamily2 = FontFamily.GenericMonospace) + { + Assert.NotSame(fontFamily1, fontFamily2); + Assert.Equal("Courier New", fontFamily2.Name); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GenericSansSerif_Get_ReturnsExpected() + { + using (FontFamily fontFamily1 = FontFamily.GenericSansSerif) + { + using (FontFamily fontFamily2 = FontFamily.GenericSansSerif) + { + Assert.NotSame(fontFamily1, fontFamily2); + Assert.Equal("Microsoft Sans Serif", fontFamily2.Name); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GenericSerif_Get_ReturnsExpected() + { + using (FontFamily fontFamily1 = FontFamily.GenericSerif) + { + using (FontFamily fontFamily2 = FontFamily.GenericSerif) + { + Assert.NotSame(fontFamily1, fontFamily2); + Assert.Equal("Times New Roman", fontFamily2.Name); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetFamilies_NullGraphics_ThrowsArgumentNullException() + { +#pragma warning disable 0618 // FontFamily.GetFamilies is deprecated. + AssertExtensions.Throws("graphics", () => FontFamily.GetFamilies(null)); +#pragma warning restore 0618 + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHashCode_Invoke_ReturnsNameHashCode() + { + using (FontFamily fontFamily = FontFamily.GenericSansSerif) + { + Assert.Equal(fontFamily.GetName(0).GetHashCode(), fontFamily.GetHashCode()); + } + } + + public static IEnumerable FontStyle_TestData() + { + yield return new object[] { FontStyle.Bold }; + yield return new object[] { FontStyle.Italic }; + yield return new object[] { FontStyle.Regular }; + yield return new object[] { FontStyle.Strikeout }; + yield return new object[] { FontStyle.Strikeout }; + yield return new object[] { FontStyle.Regular - 1 }; + yield return new object[] { FontStyle.Strikeout + 1 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FontStyle_TestData))] + public void FontFamilyProperties_CustomFont_ReturnsExpected(FontStyle style) + { + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + using (var fontFamily = new FontFamily("Code New Roman", fontCollection)) + { + Assert.True(fontFamily.IsStyleAvailable(style)); + Assert.Equal(1884, fontFamily.GetCellAscent(style)); + Assert.Equal(514, fontFamily.GetCellDescent(style)); + Assert.Equal(2048, fontFamily.GetEmHeight(style)); + Assert.Equal(2398, fontFamily.GetLineSpacing(style)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsStyleAvailable_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.IsStyleAvailable(FontStyle.Italic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetEmHeight_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.GetEmHeight(FontStyle.Italic)); + } + + private const int FrenchLCID = 1036; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, "Code New Roman")] + [InlineData(0, "Code New Roman")] + [InlineData(int.MaxValue, "Code New Roman")] + // This font has been modified to change the name to "Bonjour" if the language is French. + [InlineData(FrenchLCID, "Bonjour")] + public void GetName_LanguageCode_ReturnsExpected(int languageCode, string expectedName) + { + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.ttf")); + + using (var fontFamily = new FontFamily("Code New Roman", fontCollection)) + { + Assert.Equal(expectedName, fontFamily.GetName(languageCode)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetName_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.GetName(0)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetCellAscent_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.GetCellAscent(FontStyle.Italic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetCellDescent_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.GetCellDescent(FontStyle.Italic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetLineSpacing_Disposed_ThrowsArgumentException() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + + AssertExtensions.Throws(null, () => fontFamily.GetLineSpacing(FontStyle.Italic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Nop() + { + FontFamily fontFamily = FontFamily.GenericMonospace; + fontFamily.Dispose(); + fontFamily.Dispose(); + } + } +} diff --git a/src/System.Drawing.Common/tests/FontTests.cs b/src/System.Drawing.Common/tests/FontTests.cs new file mode 100644 index 00000000000..d61c626e88a --- /dev/null +++ b/src/System.Drawing.Common/tests/FontTests.cs @@ -0,0 +1,981 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Text; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Tests +{ + public class FontTests + { + public static IEnumerable Ctor_Family_Size_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1 }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue }; + } + + public static IEnumerable FontFamily_Equals_SameFamily_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1 }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue }; + yield return new object[] { FontFamily.GenericSansSerif, 10 }; + } + + public static IEnumerable FontFamily_Equals_DifferentFamily_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, FontFamily.GenericSerif }; + yield return new object[] { FontFamily.GenericSansSerif, FontFamily.GenericSerif }; + yield return new object[] { FontFamily.GenericSansSerif, FontFamily.GenericMonospace }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FontFamily_Equals_SameFamily_TestData))] + public void Font_Equals_SameFontFamily(FontFamily fontFamily, float size) + { + using (var font1 = new Font(fontFamily, size)) + using (var font2 = new Font(fontFamily, size)) + { + Assert.True(font1.Equals(font2)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FontFamily_Equals_DifferentFamily_TestData))] + public void Font_Equals_DifferentFontFamily(FontFamily fontFamily1, FontFamily fontFamily2) + { + using (var font1 = new Font(fontFamily1, 9)) + using (var font2 = new Font(fontFamily2, 9)) + { + // In some Linux distros all the default fonts live under the same family (Fedora, Centos, Redhat) + if (font1.FontFamily.GetHashCode() != font2.FontFamily.GetHashCode()) + Assert.False(font1.Equals(font2)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FontFamily_Equals_NullObject() + { + FontFamily nullFamily = null; + Assert.False(FontFamily.GenericMonospace.Equals(nullFamily)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_TestData))] + public void Ctor_Family_Size(FontFamily fontFamily, float emSize) + { + try + { + using (var font = new Font(fontFamily, emSize)) + { + VerifyFont(font, fontFamily.Name, emSize, FontStyle.Regular, GraphicsUnit.Point, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_TestData))] + public void Ctor_FamilyName_Size(FontFamily fontFamily, float emSize) + { + try + { + using (var font = new Font(fontFamily.Name, emSize)) + { + VerifyFont(font, fontFamily.Name, emSize, FontStyle.Regular, GraphicsUnit.Point, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + public static IEnumerable Ctor_Family_Size_Style_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1, FontStyle.Bold }; + yield return new object[] { FontFamily.GenericSerif, 2, FontStyle.Italic }; + yield return new object[] { FontFamily.GenericSansSerif, 3, FontStyle.Regular }; + yield return new object[] { FontFamily.GenericSerif, 4, FontStyle.Strikeout }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue, FontStyle.Underline }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)(-1) }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MinValue }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MaxValue }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_TestData))] + public void Ctor_Family_Size_Style(FontFamily fontFamily, float emSize, FontStyle style) + { + try + { + using (var font = new Font(fontFamily, emSize, style)) + { + VerifyFont(font, fontFamily.Name, emSize, style, GraphicsUnit.Point, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_TestData))] + public void Ctor_FamilyName_Size_Style(FontFamily fontFamily, float emSize, FontStyle style) + { + try + { + using (var font = new Font(fontFamily.Name, emSize, style)) + { + VerifyFont(font, fontFamily.Name, emSize, style, GraphicsUnit.Point, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + public static IEnumerable Ctor_Family_Size_Unit_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1, GraphicsUnit.Document }; + yield return new object[] { FontFamily.GenericSerif, 2, GraphicsUnit.Inch }; + yield return new object[] { FontFamily.GenericSansSerif, 3, GraphicsUnit.Millimeter }; + yield return new object[] { FontFamily.GenericSerif, 4, GraphicsUnit.Point }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue, GraphicsUnit.Pixel }; + yield return new object[] { FontFamily.GenericSerif, 16, GraphicsUnit.World }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Unit_TestData))] + public void Ctor_Family_Size_Unit(FontFamily fontFamily, float emSize, GraphicsUnit unit) + { + try + { + using (var font = new Font(fontFamily, emSize, unit)) + { + VerifyFont(font, fontFamily.Name, emSize, FontStyle.Regular, unit, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Unit_TestData))] + public void Ctor_FamilyName_Size_Unit(FontFamily fontFamily, float emSize, GraphicsUnit unit) + { + try + { + using (var font = new Font(fontFamily.Name, emSize, unit)) + { + VerifyFont(font, fontFamily.Name, emSize, FontStyle.Regular, unit, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + public static IEnumerable Ctor_Family_Size_Style_Unit_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1, FontStyle.Bold, GraphicsUnit.Document }; + yield return new object[] { FontFamily.GenericSerif, 2, FontStyle.Italic, GraphicsUnit.Inch }; + yield return new object[] { FontFamily.GenericSansSerif, 3, FontStyle.Regular, GraphicsUnit.Millimeter }; + yield return new object[] { FontFamily.GenericSerif, 4, FontStyle.Strikeout, GraphicsUnit.Point }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue, FontStyle.Underline, GraphicsUnit.Pixel }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)(-1), GraphicsUnit.World }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MinValue, GraphicsUnit.Millimeter }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MaxValue, GraphicsUnit.Millimeter }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_TestData))] + public void Ctor_Family_Size_Style_Unit(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit) + { + try + { + using (var font = new Font(fontFamily, emSize, style, unit)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_TestData))] + public void Ctor_FamilyName_Size_Style_Unit(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit) + { + try + { + using (var font = new Font(fontFamily.Name, emSize, style, unit)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, 1, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + public static IEnumerable Ctor_Family_Size_Style_Unit_GdiCharSet_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1, FontStyle.Bold, GraphicsUnit.Document, 0 }; + yield return new object[] { FontFamily.GenericSerif, 2, FontStyle.Italic, GraphicsUnit.Inch, 1 }; + yield return new object[] { FontFamily.GenericSansSerif, 3, FontStyle.Regular, GraphicsUnit.Millimeter, 255 }; + yield return new object[] { FontFamily.GenericSerif, 4, FontStyle.Strikeout, GraphicsUnit.Point, 10 }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue, FontStyle.Underline, GraphicsUnit.Pixel, 10 }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)(-1), GraphicsUnit.World, 8 }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MinValue, GraphicsUnit.Millimeter, 127 }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MaxValue, GraphicsUnit.Millimeter, 200 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_GdiCharSet_TestData))] + public void Ctor_Family_Size_Style_Unit_GdiCharSet(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet) + { + try + { + using (var font = new Font(fontFamily, emSize, style, unit, gdiCharSet)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, gdiCharSet, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_GdiCharSet_TestData))] + public void Ctor_FamilyName_Size_Style_Unit_GdiCharSet(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet) + { + try + { + using (var font = new Font(fontFamily.Name, emSize, style, unit, gdiCharSet)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, gdiCharSet, false); + } + } + finally + { + fontFamily.Dispose(); + } + } + + public static IEnumerable Ctor_Family_Size_Style_Unit_GdiCharSet_GdiVerticalFont_TestData() + { + yield return new object[] { FontFamily.GenericMonospace, 1, FontStyle.Bold, GraphicsUnit.Document, 0, true }; + yield return new object[] { FontFamily.GenericSerif, 2, FontStyle.Italic, GraphicsUnit.Inch, 1, false }; + yield return new object[] { FontFamily.GenericSansSerif, 3, FontStyle.Regular, GraphicsUnit.Millimeter, 255, true }; + yield return new object[] { FontFamily.GenericSerif, 4, FontStyle.Strikeout, GraphicsUnit.Point, 10, false }; + yield return new object[] { FontFamily.GenericSerif, float.MaxValue, FontStyle.Underline, GraphicsUnit.Pixel, 10, true }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)(-1), GraphicsUnit.World, 8, false }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MinValue, GraphicsUnit.Millimeter, 127, true }; + yield return new object[] { FontFamily.GenericSerif, 16, (FontStyle)int.MaxValue, GraphicsUnit.Millimeter, 200, false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_GdiCharSet_GdiVerticalFont_TestData))] + public void Ctor_Family_Size_Style_Unit_GdiCharSet_GdiVerticalFont(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + try + { + using (var font = new Font(fontFamily, emSize, style, unit, gdiCharSet, gdiVerticalFont)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, gdiCharSet, gdiVerticalFont); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Family_Size_Style_Unit_GdiCharSet_GdiVerticalFont_TestData))] + public void Ctor_FamilyName_Size_Style_Unit_GdiCharSet_GdiVerticalFont(FontFamily fontFamily, float emSize, FontStyle style, GraphicsUnit unit, byte gdiCharSet, bool gdiVerticalFont) + { + try + { + using (var font = new Font(fontFamily.Name, emSize, style, unit, gdiCharSet, gdiVerticalFont)) + { + VerifyFont(font, fontFamily.Name, emSize, style, unit, gdiCharSet, gdiVerticalFont); + } + } + finally + { + fontFamily.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_FamilyNamePrefixedWithAtSign_StripsSign() + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font($"@{family.Name}", 10)) + { + Assert.Equal(family.Name, font.Name); + Assert.Equal($"@{family.Name}", font.OriginalFontName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullFont_ThrowsNullReferenceException() + { + Assert.Throws(() => new Font(null, FontStyle.Regular)); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Ctor_DisposedFont_Success() + { + using (FontFamily family = FontFamily.GenericSerif) + { + var font = new Font(family, 10); + font.Dispose(); + + using (var copy = new Font(font, FontStyle.Italic)) + { + Assert.Equal(FontStyle.Italic, copy.Style); + Assert.Equal(family.Name, copy.Name); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullFamily_ThrowsArgumentNullException() + { + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10)); + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10, FontStyle.Italic)); + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10, GraphicsUnit.Display)); + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10, FontStyle.Italic, GraphicsUnit.Display)); + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10, FontStyle.Italic, GraphicsUnit.Display, 10)); + AssertExtensions.Throws("family", () => new Font((FontFamily)null, 10, FontStyle.Italic, GraphicsUnit.Display, 10, gdiVerticalFont: true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedFamily_ThrowsArgumentException() + { + FontFamily family = FontFamily.GenericSansSerif; + family.Dispose(); + + AssertExtensions.Throws(null, () => new Font(family, 10)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic)); + AssertExtensions.Throws(null, () => new Font(family, 10, GraphicsUnit.Display)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, GraphicsUnit.Display)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, GraphicsUnit.Display, 10)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, GraphicsUnit.Display, 10, gdiVerticalFont: true)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(float.NaN)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + public void Ctor_InvalidEmSize_ThrowsArgumentException(float emSize) + { + using (FontFamily family = FontFamily.GenericSansSerif) + { + AssertExtensions.Throws("emSize", () => new Font(family, emSize)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize)); + AssertExtensions.Throws("emSize", () => new Font(family, emSize, FontStyle.Italic)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize, FontStyle.Italic)); + AssertExtensions.Throws("emSize", () => new Font(family, emSize, GraphicsUnit.Document)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize, GraphicsUnit.Document)); + AssertExtensions.Throws("emSize", () => new Font(family, emSize, FontStyle.Italic, GraphicsUnit.Document)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize, FontStyle.Italic, GraphicsUnit.Document)); + AssertExtensions.Throws("emSize", () => new Font(family, emSize, FontStyle.Italic, GraphicsUnit.Document, 10)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize, FontStyle.Italic, GraphicsUnit.Document, 10)); + AssertExtensions.Throws("emSize", () => new Font(family, emSize, FontStyle.Italic, GraphicsUnit.Document, 10, gdiVerticalFont: true)); + AssertExtensions.Throws("emSize", () => new Font(family.Name, emSize, FontStyle.Italic, GraphicsUnit.Document, 10, gdiVerticalFont: true)); + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(GraphicsUnit.Display)] + [InlineData(GraphicsUnit.World - 1)] + [InlineData(GraphicsUnit.Millimeter + 1)] + public void Ctor_InvalidUnit_ThrowsArgumentException(GraphicsUnit unit) + { + using (FontFamily family = FontFamily.GenericSansSerif) + { + AssertExtensions.Throws(null, () => new Font(family, 10, unit)); + AssertExtensions.Throws(null, () => new Font(family.Name, 10, unit)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, unit)); + AssertExtensions.Throws(null, () => new Font(family.Name, 10, FontStyle.Italic, unit)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, unit, 10)); + AssertExtensions.Throws(null, () => new Font(family.Name, 10, FontStyle.Italic, unit, 10)); + AssertExtensions.Throws(null, () => new Font(family, 10, FontStyle.Italic, unit, 10, gdiVerticalFont: true)); + AssertExtensions.Throws(null, () => new Font(family.Name, 10, FontStyle.Italic, unit, 10, gdiVerticalFont: true)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Clone_Invoke_ReturnsExpected() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true)) + { + Font clone = Assert.IsType(font.Clone()); + Assert.NotSame(font, clone); + + Assert.Equal(font.Name, clone.FontFamily.Name); + Assert.Equal(font.Size, clone.Size); + Assert.Equal(font.Style, clone.Style); + Assert.Equal(font.Unit, clone.Unit); + Assert.Equal(font.GdiCharSet, clone.GdiCharSet); + Assert.Equal(font.GdiVerticalFont, clone.GdiVerticalFont); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_DisposedFont_ThrowsArgumentException() + { + using (FontFamily family = FontFamily.GenericSansSerif) + { + var font = new Font(family, 10); + font.Dispose(); + + AssertExtensions.Throws(null, () => font.Clone()); + } + } + + public static IEnumerable Equals_TestData() + { + FontFamily family = FontFamily.GenericSansSerif; + var font = new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true); + + yield return new object[] { font, font, true }; + yield return new object[] { font.Clone(), new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true), false }; + yield return new object[] { font.Clone(), new Font(family, 9, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true), false }; + yield return new object[] { font.Clone(), new Font(family, 10, FontStyle.Italic, GraphicsUnit.Millimeter, 10, gdiVerticalFont: true), false }; + yield return new object[] { font.Clone(), new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 9, gdiVerticalFont: true), false }; + yield return new object[] { font.Clone(), new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: false), false }; + + yield return new object[] { new Font(family, 10), new object(), false }; + yield return new object[] { new Font(family, 10), null, false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Equals_TestData))] + public void Equals_Other_ReturnsExpected(Font font, object other, bool expected) + { + // Windows7 GDI+ returns different results than later versions of Windows. + if (PlatformDetection.IsWindows7) + { + return; + } + + try + { + Assert.Equal(expected, font.Equals(other)); + Assert.Equal(font.GetHashCode(), font.GetHashCode()); + } + finally + { + font.Dispose(); + if (other is Font otherFont && !ReferenceEquals(font, otherFont)) + { + otherFont.Dispose(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdc_ZeroHdc_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Font.FromHdc(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdc_GraphicsHdc_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + AssertExtensions.Throws(null, () => Font.FromHdc(hdc)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHfont_Zero_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Font.FromHfont(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void GetHeight_Parameterless_ReturnsExpected() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10)) + { + float height = font.GetHeight(); + AssertExtensions.GreaterThan(height, 0); + + Assert.Equal((int)Math.Ceiling(height), font.Height); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void GetHeight_Graphics_ReturnsExpected() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10)) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Assert.Equal((double)font.GetHeight(graphics.DpiY), font.GetHeight(graphics), 5); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(-1, -0.1571995)] + [InlineData(1, 0.1571995)] + [InlineData(float.NaN, float.NaN)] + [InlineData(float.PositiveInfinity, float.PositiveInfinity)] + [InlineData(float.NegativeInfinity, float.NegativeInfinity)] + public void GetHeight_Dpi_ReturnsExpected(float dpi, float expected) + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10)) + { + Assert.Equal((double)expected, font.GetHeight(dpi), 5); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHeight_NullGraphics_ThrowsArgumentNullException() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10)) + { + AssertExtensions.Throws("graphics", () => font.GetHeight(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/2060")] // causes an AccessViolation in GDI+ + public void GetHeight_DisposedGraphics_ThrowsArgumentException() + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10)) + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => font.GetHeight(graphics)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHeight_Disposed_ThrowsArgumentException() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var font = new Font(family, 10); + font.Dispose(); + + AssertExtensions.Throws(null, () => font.GetHeight()); + AssertExtensions.Throws(null, () => font.GetHeight(10)); + AssertExtensions.Throws(null, () => font.GetHeight(graphics)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(FontStyle.Bold, int.MinValue, 0)] + [InlineData(FontStyle.Bold, -2147483099, 0)] + [InlineData(FontStyle.Regular, -2147483098, 0)] + [InlineData(FontStyle.Regular, -1, 0)] + [InlineData(FontStyle.Regular, 0, 0)] + [InlineData(FontStyle.Regular, 300, 0)] + [InlineData(FontStyle.Regular, 400, 0)] + [InlineData(FontStyle.Strikeout | FontStyle.Underline | FontStyle.Italic, 549, 1)] + [InlineData(FontStyle.Strikeout | FontStyle.Underline | FontStyle.Italic | FontStyle.Bold, 550, 1)] + [InlineData(FontStyle.Strikeout | FontStyle.Underline | FontStyle.Bold | FontStyle.Italic, int.MaxValue, 1)] + public void FromLogFont_ValidLogFont_ReturnsExpected(FontStyle fontStyle, int weight, byte charSet) + { + // The boundary values of the weight that is considered Bold are different between Windows 7 and Windows 8. + if (PlatformDetection.IsWindows7 || PlatformDetection.IsWindows8x) + { + return; + } + + using (FontFamily family = FontFamily.GenericMonospace) + { + var logFont = new LOGFONT + { + lfFaceName = family.Name, + lfWeight = weight, + lfItalic = (fontStyle & FontStyle.Italic) != 0 ? (byte)1 : (byte)0, + lfStrikeOut = (fontStyle & FontStyle.Strikeout) != 0 ? (byte)1 : (byte)0, + lfUnderline = (fontStyle & FontStyle.Underline) != 0 ? (byte)1 : (byte)0, + lfCharSet = charSet + }; + using (Font font = Font.FromLogFont(logFont)) + { + VerifyFont(font, family.Name, font.Size, fontStyle, GraphicsUnit.World, charSet, expectedGdiVerticalFont: false); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromLogFont_NullLogFont_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + if (PlatformDetection.IsNetFramework) + { + AssertExtensions.Throws(null, () => Font.FromLogFont(null)); + AssertExtensions.Throws(null, () => Font.FromLogFont(null, hdc)); + } + else + { + AssertExtensions.Throws("lf", () => Font.FromLogFont(null)); + AssertExtensions.Throws("lf", () => Font.FromLogFont(null, hdc)); + } + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromLogFont_InvalidLogFont_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + var logFont = new LOGFONT(); + AssertExtensions.Throws(null, () => Font.FromLogFont(logFont)); + AssertExtensions.Throws(null, () => Font.FromLogFont(logFont, hdc)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromLogFont_UnblittableStruct() + { + const byte OUT_TT_ONLY_PRECIS = 7; + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + var logFont = new UnblittableLOGFONT + { + lfOutPrecision = OUT_TT_ONLY_PRECIS + }; + + using (Font font = Font.FromLogFont(logFont)) + { + Assert.NotNull(font); + Assert.NotEmpty(font.Name); + } + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [Serializable()] + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct UnblittableLOGFONT + { + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceName; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GraphicsUnit.Document)] + [InlineData(GraphicsUnit.Inch)] + [InlineData(GraphicsUnit.Millimeter)] + [InlineData(GraphicsUnit.Pixel)] + [InlineData(GraphicsUnit.Point)] + [InlineData(GraphicsUnit.World)] + public void SizeInPoints_Get_ReturnsExpected(GraphicsUnit unit) + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10, unit)) + { + float sizeInPoints = font.SizeInPoints; + if (unit == GraphicsUnit.Point) + { + Assert.Equal(font.Size, sizeInPoints); + } + else + { + Assert.True(sizeInPoints > 0); + } + } + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [InlineData(FontStyle.Strikeout | FontStyle.Bold | FontStyle.Italic, 255, true, "@", 700)] + [InlineData(FontStyle.Regular, 0, false, "", 400)] + [InlineData(FontStyle.Regular, 10, false, "", 400)] + public void ToLogFont_Invoke_ReturnsExpected(FontStyle fontStyle, byte gdiCharSet, bool gdiVerticalFont, string expectedNamePrefix, int expectedWeight) + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10, fontStyle, GraphicsUnit.Point, gdiCharSet, gdiVerticalFont)) + { + var logFont = new LOGFONT(); + font.ToLogFont(logFont); + + Assert.Equal(-13, logFont.lfHeight); + Assert.Equal(0, logFont.lfWidth); + Assert.Equal(0, logFont.lfEscapement); + Assert.Equal(0, logFont.lfOrientation); + Assert.Equal(expectedWeight, logFont.lfWeight); + Assert.Equal(font.Italic ? 1 : 0, logFont.lfItalic); + Assert.Equal(font.Underline ? 1 : 0, logFont.lfUnderline); + Assert.Equal(font.Strikeout ? 1 : 0, logFont.lfStrikeOut); + Assert.Equal(SystemFonts.DefaultFont.GdiCharSet <= 2 ? font.GdiCharSet : SystemFonts.DefaultFont.GdiCharSet, logFont.lfCharSet); + Assert.Equal(0, logFont.lfOutPrecision); + Assert.Equal(0, logFont.lfClipPrecision); + Assert.Equal(0, logFont.lfQuality); + Assert.Equal(0, logFont.lfPitchAndFamily); + Assert.Equal($"{expectedNamePrefix}{family.Name}", logFont.lfFaceName); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(TextRenderingHint.SystemDefault)] + [InlineData(TextRenderingHint.AntiAlias)] + [InlineData(TextRenderingHint.AntiAliasGridFit)] + [InlineData(TextRenderingHint.SingleBitPerPixel)] + [InlineData(TextRenderingHint.SingleBitPerPixelGridFit)] + [InlineData(TextRenderingHint.ClearTypeGridFit)] + public void ToLogFont_InvokeGraphics_ReturnsExpected(TextRenderingHint textRenderingHint) + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10)) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.TextRenderingHint = textRenderingHint; + + var logFont = new LOGFONT(); + font.ToLogFont(logFont, graphics); + + Assert.Equal(-13, logFont.lfHeight); + Assert.Equal(0, logFont.lfWidth); + Assert.Equal(0, logFont.lfEscapement); + Assert.Equal(0, logFont.lfOrientation); + Assert.Equal(400, logFont.lfWeight); + Assert.Equal(0, logFont.lfItalic); + Assert.Equal(0, logFont.lfUnderline); + Assert.Equal(0, logFont.lfStrikeOut); + Assert.Equal(SystemFonts.DefaultFont.GdiCharSet <= 2 ? font.GdiCharSet : SystemFonts.DefaultFont.GdiCharSet, logFont.lfCharSet); + Assert.Equal(0, logFont.lfOutPrecision); + Assert.Equal(0, logFont.lfClipPrecision); + Assert.Equal(0, logFont.lfQuality); + Assert.Equal(0, logFont.lfPitchAndFamily); + Assert.Equal(family.Name, logFont.lfFaceName); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "AV Exception is wrapped in a TargetInvocationException in the .NET Framework.")] + public void ToLogFont_NullLogFont_ThrowsArgumentNullException() + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10)) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Assert.Throws(() => font.ToLogFont(null)); + Assert.Throws(() => font.ToLogFont(null, graphics)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void ToLogFont_NullGraphics_ThrowsArgumentNullException() + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10)) + { + AssertExtensions.Throws("graphics", () => font.ToLogFont(new LOGFONT(), null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToLogFont_DisposedGraphics_ThrowsArgumentException() + { + using (FontFamily family = FontFamily.GenericMonospace) + using (var font = new Font(family, 10)) + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + Assert.Throws(() => font.ToLogFont(new LOGFONT(), graphics)); + } + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public class LOGFONT + { + public int lfHeight; + public int lfWidth; + public int lfEscapement; + public int lfOrientation; + public int lfWeight; + public byte lfItalic; + public byte lfUnderline; + public byte lfStrikeOut; + public byte lfCharSet; + public byte lfOutPrecision; + public byte lfClipPrecision; + public byte lfQuality; + public byte lfPitchAndFamily; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceName; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToHfont_SimpleFont_Roundtrips() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10)) + { + IntPtr hfont = font.ToHfont(); + Assert.NotEqual(IntPtr.Zero, hfont); + Assert.NotEqual(hfont, font.ToHfont()); + + Font newFont = Font.FromHfont(hfont); + Assert.Equal(font.Name, newFont.Name); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToHfont_ComplicatedFont_DoesNotRoundtrip() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true)) + { + IntPtr hfont = font.ToHfont(); + Assert.NotEqual(IntPtr.Zero, hfont); + Assert.NotEqual(hfont, font.ToHfont()); + + Font newFont = Font.FromHfont(hfont); + Assert.NotEqual(font.Name, newFont.Name); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void ToHfont_Disposed_ThrowsArgumentException() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var font = new Font(family, 10); + font.Dispose(); + + AssertExtensions.Throws(null, () => font.ToHfont()); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void ToString_Invoke_ReturnsExpected() + { + using (FontFamily family = FontFamily.GenericSansSerif) + using (var font = new Font(family, 10, FontStyle.Bold, GraphicsUnit.Inch, 10, gdiVerticalFont: true)) + { + Assert.Equal($"[Font: Name={family.Name}, Size=10, Units=4, GdiCharSet=10, GdiVerticalFont=True]", font.ToString()); + } + } + + private static void VerifyFont(Font font, string expectedName, float expectedEmSize, FontStyle expectedStyle, GraphicsUnit expectedUnit, byte expectedGdiCharset, bool expectedGdiVerticalFont) + { + Assert.Equal(expectedName, font.Name); + Assert.Equal(expectedEmSize, font.Size); + + Assert.Equal(expectedStyle, font.Style); + Assert.Equal((expectedStyle & FontStyle.Bold) != 0, font.Bold); + Assert.Equal((expectedStyle & FontStyle.Italic) != 0, font.Italic); + Assert.Equal((expectedStyle & FontStyle.Strikeout) != 0, font.Strikeout); + Assert.Equal((expectedStyle & FontStyle.Underline) != 0, font.Underline); + + Assert.Equal(expectedUnit, font.Unit); + Assert.Equal(expectedGdiCharset, font.GdiCharSet); + Assert.Equal(expectedGdiVerticalFont, font.GdiVerticalFont); + + Assert.False(font.IsSystemFont); + Assert.Empty(font.SystemFontName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "GetHashCode doesn't include font name in .NET Framework")] + public void GetHashCode_DifferentNameSameSizeStyleUnit_HashCodeIsNotSame() + { + using FontFamily family1 = FontFamily.GenericSansSerif; + using var font1 = new Font(family1, 1, FontStyle.Bold, GraphicsUnit.Point); + + using FontFamily family2 = FontFamily.GenericMonospace; + using var font2 = new Font(family2, 1, FontStyle.Bold, GraphicsUnit.Point); + // This test depends on machine setup and whether the fonts we use are installed or not. + // If not installed we could get the same font for the two Font families we are testing for. + if (font1.Name.Equals(font2.Name, StringComparison.OrdinalIgnoreCase)) + return; + + Assert.NotEqual(font1.GetHashCode(), font2.GetHashCode()); + } + } +} diff --git a/src/System.Drawing.Common/tests/GdiPlusHandlesTests.cs b/src/System.Drawing.Common/tests/GdiPlusHandlesTests.cs new file mode 100644 index 00000000000..5aeb940cc2d --- /dev/null +++ b/src/System.Drawing.Common/tests/GdiPlusHandlesTests.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; +using Xunit.Sdk; + +namespace System.Drawing.Tests +{ + public static class GdiPlusHandlesTests + { + public static bool IsDrawingAndRemoteExecutorSupported => Helpers.GetIsDrawingSupported() && RemoteExecutor.IsSupported; + + [ConditionalFact(nameof(IsDrawingAndRemoteExecutorSupported))] + public static void GraphicsDrawIconDoesNotLeakHandles() + { + RemoteExecutor.Invoke(() => + { + const int handleTreshold = 1; + using Bitmap bmp = new(100, 100); + using Icon ico = new(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico")); + + IntPtr hdc = Helpers.GetDC(Helpers.GetForegroundWindow()); + using Graphics graphicsFromHdc = Graphics.FromHdc(hdc); + + using Process currentProcess = Process.GetCurrentProcess(); + IntPtr processHandle = currentProcess.Handle; + + int initialHandles = Helpers.GetGuiResources(processHandle, 0); + ValidateNoWin32Error(initialHandles); + + for (int i = 0; i < 5000; i++) + { + graphicsFromHdc.DrawIcon(ico, 100, 100); + } + + int finalHandles = Helpers.GetGuiResources(processHandle, 0); + ValidateNoWin32Error(finalHandles); + + Assert.InRange(finalHandles, initialHandles - handleTreshold, initialHandles + handleTreshold); + }).Dispose(); + } + + private static void ValidateNoWin32Error(int handleCount) + { + if (handleCount == 0) + { + int error = Marshal.GetLastWin32Error(); + + if (error != 0) + throw new XunitException($"GetGuiResources failed with win32 error: {error}"); + } + } + + } +} diff --git a/src/System.Drawing.Common/tests/GraphicsTests.Core.cs b/src/System.Drawing.Common/tests/GraphicsTests.Core.cs new file mode 100644 index 00000000000..4e8d23033fd --- /dev/null +++ b/src/System.Drawing.Common/tests/GraphicsTests.Core.cs @@ -0,0 +1,225 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using System.Numerics; +using Xunit; + +namespace System.Drawing.Tests +{ + public partial class GraphicsTests + { + private static Matrix3x2 s_testMatrix = Matrix3x2.CreateRotation(45) * Matrix3x2.CreateScale(2) * Matrix3x2.CreateTranslation(new Vector2(10, 20)); + + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformElements_SetNonInvertibleMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(5, 5)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Matrix3x2 matrix = new Matrix3x2(123, 24, 82, 16, 47, 30); + AssertExtensions.Throws(null, () => graphics.TransformElements = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformElements_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.TransformElements); + Assert.Throws(() => graphics.TransformElements = Matrix3x2.Identity); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformElements_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.TransformElements); + AssertExtensions.Throws(null, () => graphics.TransformElements = Matrix3x2.Identity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformElements_RoundTrip() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.TransformElements = s_testMatrix; + Assert.Equal(s_testMatrix, graphics.TransformElements); + + using (Matrix matrix = graphics.Transform) + { + Assert.Equal(s_testMatrix, matrix.MatrixElements); + } + + using (Matrix matrix = new Matrix()) + { + graphics.Transform = matrix; + Assert.True(graphics.TransformElements.IsIdentity); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_NullPen_ThrowsArgumentNullException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawRectangle(null, new RectangleF(0f, 0f, 1f, 1f))); + // other DrawRectangle overloads tested in DrawRectangle_NullPen_ThrowsArgumentNullException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_DisposedPen_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, new RectangleF(0f, 0f, 1f, 1f))); + // other DrawRectangle overloads tested in DrawRectangle_DisposedPen_ThrowsArgumentException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_Busy_ThrowsInvalidOperationException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawRectangle(pen, new RectangleF(0f, 0f, 1f, 1f))); + // other DrawRectangle overloads tested in DrawRectangle_Busy_ThrowsInvalidOperationException() + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_Disposed_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, new RectangleF(0f, 0f, 1f, 1f))); + // other DrawRectangle overloads tested in DrawRectangle_Disposed_ThrowsArgumentException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_NullPen_ThrowsArgumentNullException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("brush", () => graphics.FillPie(null, new RectangleF(0, 0, 1, 1), 0, 90)); + // other FillPie overloads tested in FillPie_NullPen_ThrowsArgumentNullException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_DisposedPen_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var brush = new SolidBrush(Color.Red); + brush.Dispose(); + + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new RectangleF(0, 0, 1, 1), 0, 90)); + // other FillPie overloads tested in FillPie_DisposedPen_ThrowsArgumentException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_ZeroWidth_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new RectangleF(0, 0, 0, 1), 0, 90)); + // other FillPie overloads tested in FillPie_ZeroWidth_ThrowsArgumentException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_ZeroHeight_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new RectangleF(0, 0, 1, 0), 0, 90)); + // other FillPie overloads tested in FillPie_ZeroHeight_ThrowsArgumentException() + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_Busy_ThrowsInvalidOperationException_Core() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.FillPie(brush, new RectangleF(0, 0, 1, 1), 0, 90)); + // other FillPie overloads tested in FillPie_Busy_ThrowsInvalidOperationException() + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_Disposed_ThrowsArgumentException_Core() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new SolidBrush(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new RectangleF(0, 0, 1, 1), 0, 90)); + // other FillPie overloads tested in FillPie_Disposed_ThrowsArgumentException() + } + } + + + + } +} diff --git a/src/System.Drawing.Common/tests/GraphicsTests.cs b/src/System.Drawing.Common/tests/GraphicsTests.cs new file mode 100644 index 00000000000..0a977b7d292 --- /dev/null +++ b/src/System.Drawing.Common/tests/GraphicsTests.cs @@ -0,0 +1,3210 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using Xunit; + +namespace System.Drawing.Tests +{ + public partial class GraphicsTests + { + public static bool IsWindows7OrWindowsArm64 => PlatformDetection.IsWindows7 || (PlatformDetection.IsWindows && PlatformDetection.IsArm64Process); + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdc_FromHdc_Roundtrips() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + IntPtr hdc = graphics.GetHdc(); + try + { + Assert.NotEqual(IntPtr.Zero, hdc); + + using (Graphics graphicsCopy = Graphics.FromHdc(hdc)) + { + VerifyGraphics(graphicsCopy, graphicsCopy.VisibleClipBounds); + } + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdc_SameImage_ReturnsSame() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics1 = Graphics.FromImage(bitmap)) + using (Graphics graphics2 = Graphics.FromImage(bitmap)) + { + try + { + Assert.Equal(graphics1.GetHdc(), graphics2.GetHdc()); + } + finally + { + graphics1.ReleaseHdc(); + graphics2.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdc_NotReleased_ThrowsInvalidOperationException() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.GetHdc()); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdc_Disposed_ThrowsObjectDisposedException() + { + using (var bitmap = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(bitmap); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.GetHdc()); + } + } + + public static IEnumerable FromHdc_TestData() + { + yield return new object[] { Helpers.GetDC(IntPtr.Zero) }; + yield return new object[] { Helpers.GetWindowDC(IntPtr.Zero) }; + + IntPtr foregroundWindow = Helpers.GetForegroundWindow(); + yield return new object[] { Helpers.GetDC(foregroundWindow) }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/51097", typeof(GraphicsTests), nameof(IsWindows7OrWindowsArm64))] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FromHdc_TestData))] + public void FromHdc_ValidHdc_ReturnsExpected(IntPtr hdc) + { + using (Graphics graphics = Graphics.FromHdc(hdc)) + { + Rectangle expected = Helpers.GetWindowDCRect(hdc); + VerifyGraphics(graphics, expected); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/51097", typeof(GraphicsTests), nameof(IsWindows7OrWindowsArm64))] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FromHdc_TestData))] + public void FromHdc_ValidHdcWithContext_ReturnsExpected(IntPtr hdc) + { + using (Graphics graphics = Graphics.FromHdc(hdc, IntPtr.Zero)) + { + Rectangle expected = Helpers.GetWindowDCRect(hdc); + VerifyGraphics(graphics, expected); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/51097", typeof(GraphicsTests), nameof(IsWindows7OrWindowsArm64))] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FromHdc_TestData))] + public void FromHdcInternal_GetDC_ReturnsExpected(IntPtr hdc) + { + using (Graphics graphics = Graphics.FromHdcInternal(hdc)) + { + Rectangle expected = Helpers.GetWindowDCRect(hdc); + VerifyGraphics(graphics, expected); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdc_ZeroHdc_ThrowsArgumentNullException() + { + AssertExtensions.Throws("hdc", () => Graphics.FromHdc(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdcInternal_ZeroHdc_ThrowsOutOfMemoryException() + { + Assert.Throws(() => Graphics.FromHdcInternal(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdc_ZeroHdc_ThrowsOutOfMemoryException() + { + Assert.Throws(() => Graphics.FromHdc(IntPtr.Zero, (IntPtr)10)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHdc_InvalidHdc_ThrowsOutOfMemoryException() + { + Assert.Throws(() => Graphics.FromHwnd((IntPtr)10)); + Assert.Throws(() => Graphics.FromHwndInternal((IntPtr)10)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHdc_ValidHdc_ResetsHdc() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + IntPtr hdc = graphics.GetHdc(); + graphics.ReleaseHdc(); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc(hdc)); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal(hdc)); + + hdc = graphics.GetHdc(); + graphics.ReleaseHdc(hdc); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc()); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal(hdc)); + + hdc = graphics.GetHdc(); + graphics.ReleaseHdcInternal(hdc); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc()); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal(hdc)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHdc_NoSuchHdc_ResetsHdc() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + IntPtr hdc = graphics.GetHdc(); + graphics.ReleaseHdc((IntPtr)10); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal((IntPtr)10)); + + hdc = graphics.GetHdc(); + graphics.ReleaseHdcInternal((IntPtr)10); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc((IntPtr)10)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHdc_OtherGraphicsHdc_Success() + { + using (var bitmap1 = new Bitmap(10, 10)) + using (var bitmap2 = new Bitmap(10, 10)) + using (Graphics graphics1 = Graphics.FromImage(bitmap1)) + using (Graphics graphics2 = Graphics.FromImage(bitmap2)) + { + IntPtr hdc1 = graphics1.GetHdc(); + IntPtr hdc2 = graphics2.GetHdc(); + Assert.NotEqual(hdc1, hdc2); + + graphics1.ReleaseHdc(hdc2); + AssertExtensions.Throws(null, () => graphics1.ReleaseHdc(hdc1)); + + graphics2.ReleaseHdc(hdc1); + AssertExtensions.Throws(null, () => graphics2.ReleaseHdc(hdc2)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHdc_NoHdc_ThrowsArgumentException() + { + using (var bitmap = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(bitmap)) + { + AssertExtensions.Throws(null, () => graphics.ReleaseHdc()); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc(IntPtr.Zero)); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal(IntPtr.Zero)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHdc_Disposed_ThrowsObjectDisposedException() + { + using (var bitmap = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(bitmap); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.ReleaseHdc()); + AssertExtensions.Throws(null, () => graphics.ReleaseHdc(IntPtr.Zero)); + AssertExtensions.Throws(null, () => graphics.ReleaseHdcInternal(IntPtr.Zero)); + } + } + + public static IEnumerable Hwnd_TestData() + { + yield return new object[] { IntPtr.Zero }; + yield return new object[] { Helpers.GetForegroundWindow() }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/51097", typeof(GraphicsTests), nameof(IsWindows7OrWindowsArm64))] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Hwnd_TestData))] + public void FromHwnd_ValidHwnd_ReturnsExpected(IntPtr hWnd) + { + using (Graphics graphics = Graphics.FromHwnd(hWnd)) + { + Rectangle expected = Helpers.GetHWndRect(hWnd); + VerifyGraphics(graphics, expected); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/51097", typeof(GraphicsTests), nameof(IsWindows7OrWindowsArm64))] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Hwnd_TestData))] + public void FromHwndInternal_ValidHwnd_ReturnsExpected(IntPtr hWnd) + { + using (Graphics graphics = Graphics.FromHwnd(hWnd)) + { + Rectangle expected = Helpers.GetHWndRect(hWnd); + VerifyGraphics(graphics, expected); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHwnd_InvalidHwnd_ThrowsOutOfMemoryException() + { + Assert.Throws(() => Graphics.FromHdc((IntPtr)10)); + Assert.Throws(() => Graphics.FromHdcInternal((IntPtr)10)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Format16bppRgb555)] + [InlineData(PixelFormat.Format16bppRgb565)] + [InlineData(PixelFormat.Format24bppRgb)] + [InlineData(PixelFormat.Format32bppArgb)] + [InlineData(PixelFormat.Format32bppPArgb)] + [InlineData(PixelFormat.Format32bppRgb)] + [InlineData(PixelFormat.Format48bppRgb)] + [InlineData(PixelFormat.Format64bppArgb)] + [InlineData(PixelFormat.Format64bppPArgb)] + public void FromImage_Bitmap_Success(PixelFormat format) + { + using (var image = new Bitmap(10, 10, format)) + using (Graphics graphics = Graphics.FromImage(image)) + { + VerifyGraphics(graphics, new Rectangle(Point.Empty, image.Size)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromImage_NullImage_ThrowsArgumentNullException() + { + AssertExtensions.Throws("image", () => Graphics.FromImage(null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Format1bppIndexed)] + [InlineData(PixelFormat.Format4bppIndexed)] + [InlineData(PixelFormat.Format8bppIndexed)] + public void FromImage_IndexedImage_ThrowsException(PixelFormat format) + { + using (var image = new Bitmap(10, 10, format)) + { + Exception exception = AssertExtensions.Throws(() => Graphics.FromImage(image)); + if (exception is ArgumentException argumentException) + Assert.Equal("image", argumentException.ParamName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromImage_DisposedImage_ThrowsArgumentException() + { + var image = new Bitmap(10, 10); + image.Dispose(); + + AssertExtensions.Throws(null, () => Graphics.FromImage(image)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromImage_Metafile_ThrowsOutOfMemoryException() + { + using (var image = new Metafile(Helpers.GetTestBitmapPath("telescope_01.wmf"))) + { + Assert.Throws(() => Graphics.FromImage(image)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.Format16bppArgb1555)] + [InlineData(PixelFormat.Format16bppGrayScale)] + public void FromImage_Invalid16BitFormat_ThrowsOutOfMemoryException(PixelFormat format) + { + using (var image = new Bitmap(10, 10, format)) + { + Assert.Throws(() => Graphics.FromImage(image)); + } + } + + public static IEnumerable CompositingMode_TestData() + { + yield return new object[] { CompositingMode.SourceCopy, Color.FromArgb(160, 255, 255, 255) }; + yield return new object[] { CompositingMode.SourceOver, Color.FromArgb(220, 185, 185, 185) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CompositingMode_TestData))] + public void CompositingMode_Set_GetReturnsExpected(CompositingMode mode, Color expectedOverlap) + { + Color transparentBlack = Color.FromArgb(160, 0, 0, 0); + Color transparentWhite = Color.FromArgb(160, 255, 255, 255); + + using (var transparentBlackBrush = new SolidBrush(transparentBlack)) + using (var transparentWhiteBrush = new SolidBrush(transparentWhite)) + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var targetImage = new Bitmap(3, 3)) + using (Graphics targetGraphics = Graphics.FromImage(targetImage)) + { + graphics.CompositingMode = mode; + Assert.Equal(mode, graphics.CompositingMode); + + graphics.FillRectangle(transparentBlackBrush, new Rectangle(0, 0, 2, 2)); + graphics.FillRectangle(transparentWhiteBrush, new Rectangle(1, 1, 2, 2)); + + targetGraphics.DrawImage(image, Point.Empty); + Helpers.VerifyBitmap(targetImage, new Color[][] + { + new Color[] { transparentBlack, transparentBlack, Helpers.EmptyColor }, + new Color[] { transparentBlack, expectedOverlap, transparentWhite }, + new Color[] { Helpers.EmptyColor, transparentWhite, transparentWhite } + }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CompositingMode.SourceOver - 1)] + [InlineData(CompositingMode.SourceCopy + 1)] + public void CompositingMode_SetInvalid_ThrowsInvalidEnumArgumentException(CompositingMode compositingMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.CompositingMode = compositingMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompositingMode_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.CompositingMode); + Assert.Throws(() => graphics.CompositingMode = CompositingMode.SourceCopy); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompositingMode_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.CompositingMode); + AssertExtensions.Throws(null, () => graphics.CompositingMode = CompositingMode.SourceCopy); + } + } + + public static IEnumerable CompositingQuality_TestData() + { + Color transparentBlack = Color.FromArgb(160, 0, 0, 0); + Color transparentWhite = Color.FromArgb(160, 255, 255, 255); + var basicExpectedColors = new Color[][] + { + new Color[] { transparentBlack, transparentBlack, Helpers.EmptyColor }, + new Color[] { transparentBlack, Color.FromArgb(220, 185, 185, 185), transparentWhite }, + new Color[] { Helpers.EmptyColor, transparentWhite, transparentWhite } + }; + + yield return new object[] { CompositingQuality.AssumeLinear, basicExpectedColors }; + yield return new object[] { CompositingQuality.Default, basicExpectedColors }; + yield return new object[] { CompositingQuality.HighSpeed, basicExpectedColors }; + yield return new object[] { CompositingQuality.Invalid, basicExpectedColors }; + + var gammaCorrectedColors = new Color[][] + { + new Color[] { Color.FromArgb(159, 0, 0, 0), Color.FromArgb(159, 0, 0, 0), Color.FromArgb(0, 0, 0, 0) }, + new Color[] { Color.FromArgb(159, 0, 0, 0), Color.FromArgb(219, 222, 222, 222), Color.FromArgb(159, 255, 255, 255) }, + new Color[] { Color.FromArgb(0, 0, 0, 0), Color.FromArgb(159, 255, 255, 255), Color.FromArgb(159, 255, 255, 255) } + }; + yield return new object[] { CompositingQuality.GammaCorrected, gammaCorrectedColors }; + yield return new object[] { CompositingQuality.HighQuality, gammaCorrectedColors }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CompositingQuality_TestData))] + public void CompositingQuality_Set_GetReturnsExpected(CompositingQuality quality, Color[][] expectedIntersectionColor) + { + Color transparentBlack = Color.FromArgb(160, 0, 0, 0); + Color transparentWhite = Color.FromArgb(160, 255, 255, 255); + + using (var transparentBlackBrush = new SolidBrush(transparentBlack)) + using (var transparentWhiteBrush = new SolidBrush(transparentWhite)) + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.CompositingQuality = quality; + Assert.Equal(quality, graphics.CompositingQuality); + + graphics.FillRectangle(transparentBlackBrush, new Rectangle(0, 0, 2, 2)); + graphics.FillRectangle(transparentWhiteBrush, new Rectangle(1, 1, 2, 2)); + + Helpers.VerifyBitmap(image, expectedIntersectionColor); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CompositingQuality.Invalid - 1)] + [InlineData(CompositingQuality.AssumeLinear + 1)] + public void CompositingQuality_SetInvalid_ThrowsInvalidEnumArgumentException(CompositingQuality compositingQuality) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.CompositingQuality = compositingQuality); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompositingQuality_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.CompositingQuality); + Assert.Throws(() => graphics.CompositingQuality = CompositingQuality.AssumeLinear); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompositingQuality_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.CompositingQuality); + AssertExtensions.Throws(null, () => graphics.CompositingQuality = CompositingQuality.AssumeLinear); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimesWithoutHdc_Success() + { + using (var bitmap = new Bitmap(10, 10)) + { + var graphics = Graphics.FromImage(bitmap); + graphics.Dispose(); + graphics.Dispose(); + + // The backing image is not disposed. + Assert.Equal(10, bitmap.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimesWithHdc_Success() + { + using (var bitmap = new Bitmap(10, 10)) + { + var graphics = Graphics.FromImage(bitmap); + graphics.GetHdc(); + + graphics.Dispose(); + graphics.Dispose(); + + // The backing image is not disposed. + Assert.Equal(10, bitmap.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DpiX_GetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DpiX); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DpiX_GetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DpiX); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DpiY_GetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DpiX); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DpiY_GetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DpiX); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(FlushIntention.Flush)] + [InlineData(FlushIntention.Sync)] + [InlineData(FlushIntention.Flush - 1)] // Not in the range of valid values of FlushIntention. + [InlineData(FlushIntention.Sync + 1)] // Not in the range of valid values of FlushIntention. + public void Flush_MultipleTimes_Success(FlushIntention intention) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + if (intention == FlushIntention.Flush) + { + graphics.Flush(); + graphics.Flush(); + } + + graphics.Flush(intention); + graphics.Flush(intention); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flush_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.Flush()); + Assert.Throws(() => graphics.Flush(FlushIntention.Sync)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Flush_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.Flush()); + AssertExtensions.Throws(null, () => graphics.Flush(FlushIntention.Flush)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(InterpolationMode.Bicubic, InterpolationMode.Bicubic)] + [InlineData(InterpolationMode.Bilinear, InterpolationMode.Bilinear)] + [InlineData(InterpolationMode.Default, InterpolationMode.Bilinear)] + [InlineData(InterpolationMode.High, InterpolationMode.HighQualityBicubic)] + [InlineData(InterpolationMode.HighQualityBicubic, InterpolationMode.HighQualityBicubic)] + [InlineData(InterpolationMode.HighQualityBilinear, InterpolationMode.HighQualityBilinear)] + [InlineData(InterpolationMode.Low, InterpolationMode.Bilinear)] + [InlineData(InterpolationMode.NearestNeighbor, InterpolationMode.NearestNeighbor)] + public void InterpolationMode_SetValid_GetReturnsExpected(InterpolationMode interpolationMode, InterpolationMode expectedInterpolationMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.InterpolationMode = interpolationMode; + Assert.Equal(expectedInterpolationMode, graphics.InterpolationMode); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(InterpolationMode.Invalid - 1)] + [InlineData(InterpolationMode.HighQualityBicubic + 1)] + public void InterpolationMode_SetInvalid_ThrowsInvalidEnumArgumentException(InterpolationMode interpolationMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.InterpolationMode = interpolationMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationMode_SetToInvalid_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.InterpolationMode = InterpolationMode.Invalid); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationMode_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.InterpolationMode); + Assert.Throws(() => graphics.InterpolationMode = InterpolationMode.HighQualityBilinear); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void InterpolationMode_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.InterpolationMode); + AssertExtensions.Throws(null, () => graphics.InterpolationMode = InterpolationMode.HighQualityBilinear); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(1000000032)] + [InlineData(float.NaN)] + public void PageScale_SetValid_GetReturnsExpected(float pageScale) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.PageScale = pageScale; + Assert.Equal(pageScale, graphics.PageScale); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1000000033)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + public void PageScale_SetInvalid_ThrowsArgumentException(float pageScale) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.PageScale = pageScale); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PageScale_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.PageScale); + Assert.Throws(() => graphics.PageScale = 10); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PageScale_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.PageScale); + AssertExtensions.Throws(null, () => graphics.PageScale = 10); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GraphicsUnit.Display)] + [InlineData(GraphicsUnit.Document)] + [InlineData(GraphicsUnit.Inch)] + [InlineData(GraphicsUnit.Millimeter)] + [InlineData(GraphicsUnit.Pixel)] + [InlineData(GraphicsUnit.Point)] + public void PageUnit_SetValid_GetReturnsExpected(GraphicsUnit pageUnit) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.PageUnit = pageUnit; + Assert.Equal(pageUnit, graphics.PageUnit); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GraphicsUnit.World - 1)] + [InlineData(GraphicsUnit.Millimeter + 1)] + public void PageUnit_SetInvalid_ThrowsInvalidEnumArgumentException(GraphicsUnit pageUnit) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.PageUnit = pageUnit); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PageUnit_SetWorld_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.PageUnit = GraphicsUnit.World); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PageUnit_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.PageUnit); + Assert.Throws(() => graphics.PageUnit = GraphicsUnit.Document); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PageUnit_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.PageUnit); + AssertExtensions.Throws(null, () => graphics.PageUnit = GraphicsUnit.Document); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelOffsetMode.Default)] + [InlineData(PixelOffsetMode.Half)] + [InlineData(PixelOffsetMode.HighQuality)] + [InlineData(PixelOffsetMode.HighSpeed)] + [InlineData(PixelOffsetMode.None)] + public void PixelOffsetMode_SetValid_GetReturnsExpected(PixelOffsetMode pixelOffsetMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.PixelOffsetMode = pixelOffsetMode; + Assert.Equal(pixelOffsetMode, graphics.PixelOffsetMode); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelOffsetMode.Invalid - 1)] + [InlineData(PixelOffsetMode.Half + 1)] + public void PixelOffsetMode_SetInvalid_ThrowsInvalidEnumArgumentException(PixelOffsetMode pixelOffsetMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.PixelOffsetMode = pixelOffsetMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PixelOffsetMode_SetToInvalid_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.PixelOffsetMode = PixelOffsetMode.Invalid); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PixelOffsetMode_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.PixelOffsetMode); + Assert.Throws(() => graphics.PixelOffsetMode = PixelOffsetMode.Default); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PixelOffsetMode_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.PixelOffsetMode); + AssertExtensions.Throws(null, () => graphics.PixelOffsetMode = PixelOffsetMode.Default); + } + } + + public static IEnumerable RenderingOrigin_TestData() + { + Color empty = Color.FromArgb(255, 0, 0, 0); + Color red = Color.FromArgb(Color.Red.ToArgb()); + + yield return new object[] + { + new Point(0, 0), + new Color[][] + { + new Color[] { red, red, red }, + new Color[] { red, empty, empty }, + new Color[] { red, empty, empty } + } + }; + + yield return new object[] + { + new Point(1, 1), + new Color[][] + { + new Color[] { empty, red, empty }, + new Color[] { red, red, red }, + new Color[] { empty, red, empty } + } + }; + + var allEmpty = new Color[][] + { + new Color[] { empty, empty, empty }, + new Color[] { empty, empty, empty }, + new Color[] { empty, empty, empty } + }; + + yield return new object[] { new Point(-3, -3), allEmpty }; + yield return new object[] { new Point(3, 3), allEmpty }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(RenderingOrigin_TestData))] + public void RenderingOrigin_SetToCustom_RendersExpected(Point renderingOrigin, Color[][] expectedRendering) + { + Color red = Color.FromArgb(Color.Red.ToArgb()); + + using (var image = new Bitmap(3, 3)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new HatchBrush(HatchStyle.Cross, red)) + { + graphics.RenderingOrigin = renderingOrigin; + Assert.Equal(renderingOrigin, graphics.RenderingOrigin); + + graphics.FillRectangle(brush, new Rectangle(0, 0, 3, 3)); + Helpers.VerifyBitmap(image, expectedRendering); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RenderingOrigin_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.RenderingOrigin); + Assert.Throws(() => graphics.RenderingOrigin = Point.Empty); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RenderingOrigin_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.RenderingOrigin); + AssertExtensions.Throws(null, () => graphics.RenderingOrigin = Point.Empty); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(SmoothingMode.AntiAlias, SmoothingMode.AntiAlias)] + [InlineData(SmoothingMode.Default, SmoothingMode.None)] + [InlineData(SmoothingMode.HighQuality, SmoothingMode.AntiAlias)] + [InlineData(SmoothingMode.HighSpeed, SmoothingMode.None)] + [InlineData(SmoothingMode.None, SmoothingMode.None)] + public void SmoothingMode_SetValid_GetReturnsExpected(SmoothingMode smoothingMode, SmoothingMode expectedSmoothingMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.SmoothingMode = smoothingMode; + Assert.Equal(expectedSmoothingMode, graphics.SmoothingMode); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(SmoothingMode.Invalid - 1)] + [InlineData(SmoothingMode.AntiAlias + 1)] + public void SmoothingMode_SetInvalid_ThrowsInvalidEnumArgumentException(SmoothingMode smoothingMode) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.SmoothingMode = smoothingMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SmoothingMode_SetToInvalid_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.SmoothingMode = SmoothingMode.Invalid); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SmoothingMode_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.SmoothingMode); + Assert.Throws(() => graphics.SmoothingMode = SmoothingMode.AntiAlias); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SmoothingMode_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.SmoothingMode); + AssertExtensions.Throws(null, () => graphics.SmoothingMode = SmoothingMode.AntiAlias); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(12)] + public void TextContrast_SetValid_GetReturnsExpected(int textContrast) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.TextContrast = textContrast; + Assert.Equal(textContrast, graphics.TextContrast); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(13)] + public void TextContrast_SetInvalid_ThrowsArgumentException(int textContrast) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.TextContrast = textContrast); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TextContrast_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.TextContrast); + Assert.Throws(() => graphics.TextContrast = 5); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TextContrast_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.TextContrast); + AssertExtensions.Throws(null, () => graphics.TextContrast = 5); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(TextRenderingHint.AntiAlias)] + [InlineData(TextRenderingHint.AntiAliasGridFit)] + [InlineData(TextRenderingHint.ClearTypeGridFit)] + [InlineData(TextRenderingHint.SingleBitPerPixel)] + [InlineData(TextRenderingHint.SingleBitPerPixelGridFit)] + [InlineData(TextRenderingHint.SystemDefault)] + public void TextRenderingHint_SetValid_GetReturnsExpected(TextRenderingHint textRenderingHint) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.TextRenderingHint = textRenderingHint; + Assert.Equal(textRenderingHint, graphics.TextRenderingHint); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(TextRenderingHint.SystemDefault - 1)] + [InlineData(TextRenderingHint.ClearTypeGridFit + 1)] + public void TextRenderingHint_SetInvalid_ThrowsInvalidEnumArgumentException(TextRenderingHint textRenderingHint) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("value", () => graphics.TextRenderingHint = textRenderingHint); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TextRenderingHint_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.TextRenderingHint); + Assert.Throws(() => graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TextRenderingHint_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.TextRenderingHint); + AssertExtensions.Throws(null, () => graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetValid_GetReturnsExpected() + { + Color empty = Helpers.EmptyColor; + Color red = Color.FromArgb(Color.Red.ToArgb()); + + using (var image = new Bitmap(5, 5)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(red)) + using (var matrix = new Matrix()) + { + matrix.Scale(1f / 3, 2); + matrix.Translate(2, 1); + matrix.Rotate(270); + + graphics.Transform = matrix; + graphics.FillRectangle(brush, new Rectangle(0, 0, 3, 2)); + Helpers.VerifyBitmap(image, new Color[][] + { + new Color[] { empty, red, empty, empty, empty }, + new Color[] { empty, red, empty, empty, empty }, + new Color[] { empty, empty, empty, empty, empty }, + new Color[] { empty, empty, empty, empty, empty }, + new Color[] { empty, empty, empty, empty, empty } + }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNull_ThrowsNullReferenceException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Assert.Throws(() => graphics.Transform = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetDisposedMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var matrix = new Matrix(); + matrix.Dispose(); + + AssertExtensions.Throws(null, () => graphics.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNonInvertibleMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(5, 5)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => graphics.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_GetSetWhenBusy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var matrix = new Matrix()) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.Transform); + Assert.Throws(() => graphics.Transform = matrix); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.Transform); + AssertExtensions.Throws(null, () => graphics.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Invoke_SetsTransformToIdentity() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Assert.False(graphics.Transform.IsIdentity); + + graphics.ResetTransform(); + Assert.True(graphics.Transform.IsIdentity); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.ResetTransform()); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.ResetTransform()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NoOrder_Success() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Multiply(matrix); + + graphics.MultiplyTransform(matrix); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend)] + [InlineData(MatrixOrder.Append)] + public void MultiplyTransform_Order_Success(MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Multiply(matrix, order); + + graphics.MultiplyTransform(matrix, order); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NullMatrix_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("matrix", () => graphics.MultiplyTransform(null)); + AssertExtensions.Throws("matrix", () => graphics.MultiplyTransform(null, MatrixOrder.Append)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_DisposedMatrix_Nop() + { + var brush = new LinearGradientBrush(new Rectangle(1, 2, 3, 4), Color.Plum, Color.Red, 45, true); + Matrix transform = brush.Transform; + + var matrix = new Matrix(); + matrix.Dispose(); + + brush.MultiplyTransform(matrix); + brush.MultiplyTransform(matrix, MatrixOrder.Append); + + Assert.Equal(transform, brush.Transform); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NonInvertibleMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => graphics.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => graphics.MultiplyTransform(matrix, MatrixOrder.Append)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void MultiplyTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => graphics.MultiplyTransform(matrix, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var matrix = new Matrix()) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.MultiplyTransform(matrix)); + Assert.Throws(() => graphics.MultiplyTransform(matrix, MatrixOrder.Append)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => graphics.MultiplyTransform(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, -2)] + [InlineData(0, 0)] + [InlineData(1, 2)] + public void TranslateTransform_NoOrder_Success(float dx, float dy) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Translate(dx, dy); + + graphics.TranslateTransform(dx, dy); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1, MatrixOrder.Prepend)] + [InlineData(1, 1, MatrixOrder.Append)] + [InlineData(0, 0, MatrixOrder.Prepend)] + [InlineData(0, 0, MatrixOrder.Append)] + [InlineData(-1, -1, MatrixOrder.Prepend)] + [InlineData(-1, -1, MatrixOrder.Append)] + public void TranslateTransform_Order_Success(float dx, float dy, MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Translate(dx, dy, order); + + graphics.TranslateTransform(dx, dy, order); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void TranslateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.TranslateTransform(0, 0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.TranslateTransform(0, 0)); + Assert.Throws(() => graphics.TranslateTransform(0, 0, MatrixOrder.Append)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.TranslateTransform(0, 0)); + AssertExtensions.Throws(null, () => graphics.TranslateTransform(0, 0, MatrixOrder.Append)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, -2)] + [InlineData(1, 2)] + public void ScaleTransform_NoOrder_Success(float sx, float sy) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Scale(sx, sy); + + graphics.ScaleTransform(sx, sy); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 1, MatrixOrder.Prepend)] + [InlineData(1, 1, MatrixOrder.Append)] + [InlineData(-1, -1, MatrixOrder.Prepend)] + [InlineData(-1, -1, MatrixOrder.Append)] + public void ScaleTransform_Order_Success(float sx, float sy, MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Scale(sx, sy, order); + + graphics.ScaleTransform(sx, sy, order); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_ZeroZero_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.ScaleTransform(0, 0)); + AssertExtensions.Throws(null, () => graphics.ScaleTransform(0, 0, MatrixOrder.Append)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void ScaleTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.ScaleTransform(0, 0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.ScaleTransform(0, 0)); + Assert.Throws(() => graphics.ScaleTransform(0, 0, MatrixOrder.Append)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.ScaleTransform(0, 0)); + AssertExtensions.Throws(null, () => graphics.ScaleTransform(0, 0, MatrixOrder.Append)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(360)] + public void RotateTransform_NoOrder_Success(float angle) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Rotate(angle); + + graphics.RotateTransform(angle); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, MatrixOrder.Prepend)] + [InlineData(1, MatrixOrder.Append)] + [InlineData(0, MatrixOrder.Prepend)] + [InlineData(360, MatrixOrder.Append)] + [InlineData(-1, MatrixOrder.Prepend)] + [InlineData(-1, MatrixOrder.Append)] + public void RotateTransform_Order_Success(float angle, MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + Matrix expectedTransform = graphics.Transform; + expectedTransform.Rotate(angle, order); + + graphics.RotateTransform(angle, order); + Assert.Equal(expectedTransform, graphics.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void RotateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder order) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.RotateTransform(0, order)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.RotateTransform(0)); + Assert.Throws(() => graphics.RotateTransform(0, MatrixOrder.Append)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.RotateTransform(0)); + AssertExtensions.Throws(null, () => graphics.RotateTransform(0, MatrixOrder.Append)); + } + } + + public static IEnumerable CopyFromScreen_TestData() + { + yield return new object[] { 0, 0, 0, 0, new Size(0, 0) }; + yield return new object[] { -1, -1, 0, 0, new Size(1, 1) }; + yield return new object[] { int.MaxValue, int.MaxValue, 0, 0, new Size(1, 1) }; + yield return new object[] { int.MaxValue, int.MaxValue, 0, 0, new Size(1, 1) }; + yield return new object[] { 0, 0, -1, -1, new Size(1, 1) }; + yield return new object[] { 0, 0, int.MaxValue, int.MaxValue, new Size(1, 1) }; + yield return new object[] { 0, 0, 0, 0, new Size(-1, -1) }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CopyFromScreen_TestData))] + public void CopyFromScreen_OutOfRange_DoesNotAffectGraphics(int sourceX, int sourceY, int destinationX, int destinationY, Size size) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Color plum = Color.FromArgb(Color.Plum.ToArgb()); + image.SetPixel(0, 0, plum); + + graphics.CopyFromScreen(sourceX, sourceY, destinationX, destinationY, size); + graphics.CopyFromScreen(new Point(sourceX, sourceY), new Point(destinationX, destinationY), size); + Assert.Equal(plum, image.GetPixel(0, 0)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0, 0, 0, 10, 10)] + [InlineData(0, 0, 0, 0, int.MaxValue, int.MaxValue)] + [InlineData(1, 1, 2, 2, 3, 3)] + public void CopyFromScreen_ValidRange_AffectsGraphics(int sourceX, int sourceY, int destinationX, int destinationY, int width, int height) + { + Color color = Color.FromArgb(2, 3, 4); + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (SolidBrush brush = new SolidBrush(color)) + { + graphics.FillRectangle(brush, new Rectangle(0, 0, 10, 10)); + graphics.CopyFromScreen(sourceX, sourceY, destinationX, destinationY, new Size(width, height)); + + Rectangle drawnRect = new Rectangle(destinationX, destinationY, width, height); + for (int y = 0; y < image.Height; y++) + { + for (int x = 0; x < image.Width; x++) + { + Color imageColor = image.GetPixel(x, y); + if (!drawnRect.Contains(x, y)) + { + Assert.Equal(color, imageColor); + } + else + { + Assert.NotEqual(color, imageColor); + } + } + } + } + } + + public static IEnumerable CopyPixelOperation_TestData() + { + yield return new object[] { CopyPixelOperation.NoMirrorBitmap }; + yield return new object[] { CopyPixelOperation.Blackness }; + yield return new object[] { CopyPixelOperation.NotSourceErase }; + yield return new object[] { CopyPixelOperation.NotSourceCopy }; + yield return new object[] { CopyPixelOperation.SourceErase }; + yield return new object[] { CopyPixelOperation.DestinationInvert }; + yield return new object[] { CopyPixelOperation.PatInvert }; + yield return new object[] { CopyPixelOperation.SourceInvert }; + yield return new object[] { CopyPixelOperation.SourceAnd }; + yield return new object[] { CopyPixelOperation.MergePaint }; + yield return new object[] { CopyPixelOperation.MergeCopy }; + yield return new object[] { CopyPixelOperation.SourceCopy }; + yield return new object[] { CopyPixelOperation.SourcePaint }; + yield return new object[] { CopyPixelOperation.SourceCopy }; + yield return new object[] { CopyPixelOperation.PatCopy }; + yield return new object[] { CopyPixelOperation.PatPaint }; + yield return new object[] { CopyPixelOperation.Whiteness }; + yield return new object[] { CopyPixelOperation.CaptureBlt }; + yield return new object[] { CopyPixelOperation.CaptureBlt }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CopyPixelOperation_TestData))] + public void CopyFromScreen_IntsAndValidCopyPixelOperation_Success(CopyPixelOperation copyPixelOperation) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + // We don't know what the screen looks like at this point in time, so + // just make sure that this doesn't fail. + graphics.CopyFromScreen(0, 0, 0, 0, new Size(1, 1), copyPixelOperation); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CopyPixelOperation_TestData))] + public void CopyFromScreen_PointsAndValidCopyPixelOperation_Success(CopyPixelOperation copyPixelOperation) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + // We don't know what the screen looks like at this point in time, so + // just make sure that this doesn't fail. + graphics.CopyFromScreen(Point.Empty, Point.Empty, new Size(1, 1), copyPixelOperation); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CopyPixelOperation.NoMirrorBitmap + 1)] + [InlineData(CopyPixelOperation.Blackness - 1)] + [InlineData(CopyPixelOperation.NotSourceErase - 1)] + [InlineData(CopyPixelOperation.NotSourceCopy - 1)] + [InlineData(CopyPixelOperation.SourceErase - 1)] + [InlineData(CopyPixelOperation.DestinationInvert - 1)] + [InlineData(CopyPixelOperation.PatInvert - 1)] + [InlineData(CopyPixelOperation.SourceInvert - 1)] + [InlineData(CopyPixelOperation.SourceAnd - 1)] + [InlineData(CopyPixelOperation.MergePaint - 1)] + [InlineData(CopyPixelOperation.MergeCopy - 1)] + [InlineData(CopyPixelOperation.SourceCopy - 1)] + [InlineData(CopyPixelOperation.SourcePaint - 1)] + [InlineData(CopyPixelOperation.PatCopy - 1)] + [InlineData(CopyPixelOperation.PatPaint - 1)] + [InlineData(CopyPixelOperation.Whiteness - 1)] + [InlineData(CopyPixelOperation.CaptureBlt - 1)] + [InlineData(CopyPixelOperation.CaptureBlt + 1)] + public void CopyFromScreen_InvalidCopyPixelOperation_ThrowsInvalidEnumArgumentException(CopyPixelOperation copyPixelOperation) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Assert.ThrowsAny(() => graphics.CopyFromScreen(1, 2, 3, 4, Size.Empty, copyPixelOperation)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CopyFromScreen_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.CopyFromScreen(0, 0, 0, 0, Size.Empty)); + Assert.Throws(() => graphics.CopyFromScreen(0, 0, 0, 0, Size.Empty, CopyPixelOperation.DestinationInvert)); + Assert.Throws(() => graphics.CopyFromScreen(Point.Empty, Point.Empty, Size.Empty)); + Assert.Throws(() => graphics.CopyFromScreen(Point.Empty, Point.Empty, Size.Empty, CopyPixelOperation.DestinationInvert)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/23375")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CopyFromScreen_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.CopyFromScreen(0, 0, 0, 0, Size.Empty)); + AssertExtensions.Throws(null, () => graphics.CopyFromScreen(0, 0, 0, 0, Size.Empty, CopyPixelOperation.MergeCopy)); + AssertExtensions.Throws(null, () => graphics.CopyFromScreen(Point.Empty, Point.Empty, Size.Empty)); + AssertExtensions.Throws(null, () => graphics.CopyFromScreen(Point.Empty, Point.Empty, Size.Empty, CopyPixelOperation.MergeCopy)); + } + } + + public static IEnumerable TransformPoints_TestData() + { + yield return new object[] + { + CoordinateSpace.Device, + CoordinateSpace.Page, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(1, 1), new Point(2, 2) } + }; + + yield return new object[] + { + CoordinateSpace.Device, + CoordinateSpace.World, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(9, 12), new Point(13, 18) } + }; + + yield return new object[] + { + CoordinateSpace.Page, + CoordinateSpace.Device, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(1, 1), new Point(2, 2) } + }; + + yield return new object[] + { + CoordinateSpace.Page, + CoordinateSpace.World, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(9, 12), new Point(13, 18) } + }; + + yield return new object[] + { + CoordinateSpace.World, + CoordinateSpace.Device, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(1, -1), new Point(0, -1) } + }; + + yield return new object[] + { + CoordinateSpace.World, + CoordinateSpace.Page, + new Point[] { new Point(1, 1), new Point(2, 2) }, + new Point[] { new Point(1, -1), new Point(0, -1) } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformPoints_TestData))] + public void TransformPoints_Points_Success(CoordinateSpace destSpace, CoordinateSpace srcSpace, Point[] points, Point[] expected) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.PageScale = 10; + graphics.Transform = transform; + + graphics.TransformPoints(destSpace, srcSpace, points); + Assert.Equal(expected, points); + } + } + + public static IEnumerable TransformPointFs_TestData() + { + yield return new object[] + { + CoordinateSpace.Device, + CoordinateSpace.Page, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new Point(1, 1), new Point(2, 2) } + }; + + yield return new object[] + { + CoordinateSpace.Device, + CoordinateSpace.World, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new Point(9, 12), new Point(13, 18) } + }; + + yield return new object[] + { + CoordinateSpace.Page, + CoordinateSpace.Device, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new Point(1, 1), new Point(2, 2) } + }; + + yield return new object[] + { + CoordinateSpace.Page, + CoordinateSpace.World, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new Point(9, 12), new Point(13, 18) } + }; + + yield return new object[] + { + CoordinateSpace.World, + CoordinateSpace.Device, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new PointF(0.5f, -1.5f), new Point(0, -1) } + }; + + yield return new object[] + { + CoordinateSpace.World, + CoordinateSpace.Page, + new PointF[] { new Point(1, 1), new Point(2, 2) }, + new PointF[] { new PointF(0.5f, -1.5f), new Point(0, -1) } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TransformPointFs_TestData))] + public void TransformPoints_PointFs_Success(CoordinateSpace destSpace, CoordinateSpace srcSpace, PointF[] points, PointF[] expected) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.PageScale = 10; + graphics.Transform = transform; + + graphics.TransformPoints(destSpace, srcSpace, points); + Assert.Equal(expected, points); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CoordinateSpace.Device)] + [InlineData(CoordinateSpace.World)] + [InlineData(CoordinateSpace.Page)] + public void TransformPoints_PointsAndSameCoordinateSpace_DoesNothing(CoordinateSpace space) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + + var points = new Point[] { new Point(1, 1) }; + graphics.TransformPoints(space, space, points); + Assert.Equal(new Point[] { new Point(1, 1) }, points); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CoordinateSpace.Device)] + [InlineData(CoordinateSpace.World)] + [InlineData(CoordinateSpace.Page)] + public void TransformPoints_PointFsAndSameCoordinateSpace_DoesNothing(CoordinateSpace space) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + graphics.Transform = transform; + + var points = new PointF[] { new PointF(1, 1) }; + graphics.TransformPoints(space, space, points); + Assert.Equal(new PointF[] { new PointF(1, 1) }, points); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CoordinateSpace.World - 1)] + [InlineData(CoordinateSpace.Device + 1)] + public void TransformPoints_InvalidDestSpace_ThrowsArgumentException(CoordinateSpace destSpace) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.TransformPoints(destSpace, CoordinateSpace.World, new Point[] { new Point(1, 1) })); + AssertExtensions.Throws(null, () => graphics.TransformPoints(destSpace, CoordinateSpace.World, new PointF[] { new PointF(1, 1) })); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(CoordinateSpace.World - 1)] + [InlineData(CoordinateSpace.Device + 1)] + public void TransformPoints_InvalidSourceSpace_ThrowsArgumentException(CoordinateSpace srcSpace) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.World, srcSpace, new Point[] { new Point(1, 1) })); + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.World, srcSpace, new PointF[] { new PointF(1, 1) })); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pts", () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, (Point[])null)); + AssertExtensions.Throws("pts", () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, (PointF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_EmptyPoints_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new Point[0])); + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new PointF[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new Point[] { Point.Empty })); + Assert.Throws(() => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new PointF[] { PointF.Empty })); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TransformPoints_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new Point[] { Point.Empty })); + AssertExtensions.Throws(null, () => graphics.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Page, new PointF[] { PointF.Empty })); + } + } + + public static IEnumerable GetNearestColor_TestData() + { + yield return new object[] { PixelFormat.Format32bppArgb, Color.Red, Color.FromArgb(Color.Red.ToArgb()) }; + yield return new object[] { PixelFormat.Format16bppRgb555, Color.Red, Color.FromArgb(255, 248, 0, 0) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(GetNearestColor_TestData))] + public void GetNearestColor_Color_ReturnsExpected(PixelFormat pixelFormat, Color color, Color expected) + { + using (var image = new Bitmap(10, 10, pixelFormat)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Assert.Equal(expected, graphics.GetNearestColor(color)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetNearestColor_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.GetNearestColor(Color.Red)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetNearestColor_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.GetNearestColor(Color.Red)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawArc(null, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawArc(null, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawArc(null, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawArc(null, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_ZeroWidth_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 0, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 0, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 0, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 0f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_ZeroHeight_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 0), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 0, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 0), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 0f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + Assert.Throws(() => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); + Assert.Throws(() => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + Assert.Throws(() => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawArc_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawRectangle(null, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws("pen", () => graphics.DrawRectangle(null, 0, 0, 1, 1)); + AssertExtensions.Throws("pen", () => graphics.DrawRectangle(null, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, 0, 0, 1, 1)); + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawRectangle(pen, new Rectangle(0, 0, 1, 1))); + Assert.Throws(() => graphics.DrawRectangle(pen, 0, 0, 1, 1)); + Assert.Throws(() => graphics.DrawRectangle(pen, 0f, 0f, 1f, 1f)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangle_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, 0, 0, 1, 1)); + AssertExtensions.Throws(null, () => graphics.DrawRectangle(pen, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawRectangles(null, new Rectangle[2])); + AssertExtensions.Throws("pen", () => graphics.DrawRectangles(null, new RectangleF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new Rectangle[2])); + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new RectangleF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_NullRectangles_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("rects", () => graphics.DrawRectangles(pen, (Rectangle[])null)); + AssertExtensions.Throws("rects", () => graphics.DrawRectangles(pen, (RectangleF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_EmptyRectangles_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new Rectangle[0])); + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new RectangleF[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawRectangles(pen, new Rectangle[2])); + Assert.Throws(() => graphics.DrawRectangles(pen, new RectangleF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawRectangles_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new Rectangle[2])); + AssertExtensions.Throws(null, () => graphics.DrawRectangles(pen, new RectangleF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawEllipse_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawEllipse(null, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws("pen", () => graphics.DrawEllipse(null, 0, 0, 1, 1)); + AssertExtensions.Throws("pen", () => graphics.DrawEllipse(null, new RectangleF(0, 0, 1, 1))); + AssertExtensions.Throws("pen", () => graphics.DrawEllipse(null, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawEllipse_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, 0, 0, 1, 1)); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, new RectangleF(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawEllipse_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawEllipse(pen, new Rectangle(0, 0, 1, 1))); + Assert.Throws(() => graphics.DrawEllipse(pen, 0, 0, 1, 1)); + Assert.Throws(() => graphics.DrawEllipse(pen, new RectangleF(0, 0, 1, 1))); + Assert.Throws(() => graphics.DrawEllipse(pen, 0f, 0f, 1f, 1f)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawEllipse_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, new Rectangle(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, 0, 0, 1, 1)); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, new RectangleF(0, 0, 1, 1))); + AssertExtensions.Throws(null, () => graphics.DrawEllipse(pen, 0f, 0f, 1f, 1f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawPie(null, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawPie(null, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawPie(null, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws("pen", () => graphics.DrawPie(null, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_ZeroWidth_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new Rectangle(0, 0, 0, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0, 0, 0, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new RectangleF(0, 0, 0, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0f, 0f, 0f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_ZeroHeight_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 0), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 0, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 0), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 0f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawPie(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + Assert.Throws(() => graphics.DrawPie(pen, 0, 0, 1, 1, 0, 90)); + Assert.Throws(() => graphics.DrawPie(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + Assert.Throws(() => graphics.DrawPie(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPie_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawPie(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPolygon_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawPolygon(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawPolygon(null, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPolygon_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPolygon_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawPolygon(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawPolygon(pen, (PointF[])null)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void DrawPolygon_InvalidPointsLength_ThrowsArgumentException(int length) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new Point[length])); + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new PointF[length])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPolygon_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawPolygon(pen, new Point[2])); + Assert.Throws(() => graphics.DrawPolygon(pen, new PointF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPolygon_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawPolygon(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var graphicsPath = new GraphicsPath()) + { + AssertExtensions.Throws("pen", () => graphics.DrawPath(null, graphicsPath)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var graphicsPath = new GraphicsPath()) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPath(pen, graphicsPath)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_NullPath_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("path", () => graphics.DrawPath(pen, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_DisposedPath_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + var graphicsPath = new GraphicsPath(); + graphicsPath.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPath(pen, graphicsPath)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + using (var graphicsPath = new GraphicsPath()) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawPath(pen, graphicsPath)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawPath_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + using (var graphicsPath = new GraphicsPath()) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawPath(pen, graphicsPath)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawCurve_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new PointF[2])); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new Point[2], 1)); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new PointF[2], 1)); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new PointF[2], 0, 2)); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new Point[2], 0, 2, 1)); + AssertExtensions.Throws("pen", () => graphics.DrawCurve(null, new PointF[2], 0, 2, 1)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawCurve_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 0, 2)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2], 0, 2, 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 0, 2, 1)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawCurve_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (PointF[])null)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (Point[])null, 1)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (PointF[])null, 1)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (PointF[])null, 0, 2)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (Point[])null, 0, 2, 1)); + AssertExtensions.Throws("points", () => graphics.DrawCurve(pen, (PointF[])null, 0, 2, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void DrawCurve_InvalidPointsLength_ThrowsArgumentException(int length) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[length])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[length], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length], 0, length)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[length], 0, length, 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length], 0, length, 1)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(4, -1, 4)] + [InlineData(4, 0, -1)] + [InlineData(4, 4, 0)] + [InlineData(4, 0, 5)] + [InlineData(4, 3, 2)] + public void DrawCurve_InvalidOffsetCount_ThrowsArgumentException(int length, int offset, int numberOfSegments) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length], offset, numberOfSegments)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[length], offset, numberOfSegments, 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[length], offset, numberOfSegments, 1)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawCurve_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawCurve(pen, new Point[2])); + Assert.Throws(() => graphics.DrawCurve(pen, new PointF[2])); + Assert.Throws(() => graphics.DrawCurve(pen, new Point[2], 1)); + Assert.Throws(() => graphics.DrawCurve(pen, new PointF[2], 1)); + Assert.Throws(() => graphics.DrawCurve(pen, new PointF[2], 0, 2)); + Assert.Throws(() => graphics.DrawCurve(pen, new Point[2], 0, 2, 1)); + Assert.Throws(() => graphics.DrawCurve(pen, new PointF[2], 0, 2, 1)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawCurve_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2])); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 0, 2)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new Point[2], 0, 2, 1)); + AssertExtensions.Throws(null, () => graphics.DrawCurve(pen, new PointF[2], 0, 2, 1)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawClosedCurve_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawClosedCurve(null, new Point[3])); + AssertExtensions.Throws("pen", () => graphics.DrawClosedCurve(null, new Point[3], 1, FillMode.Winding)); + AssertExtensions.Throws("pen", () => graphics.DrawClosedCurve(null, new PointF[3])); + AssertExtensions.Throws("pen", () => graphics.DrawClosedCurve(null, new PointF[3], 1, FillMode.Winding)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawClosedCurve_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[3])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[3], 1, FillMode.Winding)); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[3])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[3], 1, FillMode.Winding)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawClosedCurve_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawClosedCurve(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawClosedCurve(pen, (Point[])null, 1, FillMode.Winding)); + AssertExtensions.Throws("points", () => graphics.DrawClosedCurve(pen, (PointF[])null)); + AssertExtensions.Throws("points", () => graphics.DrawClosedCurve(pen, (PointF[])null, 1, FillMode.Winding)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void DrawClosedCurve_InvalidPointsLength_ThrowsArgumentException(int length) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[length])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[length], 1, FillMode.Winding)); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[length])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[length], 1, FillMode.Winding)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawClosedCurve_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawClosedCurve(pen, new Point[3])); + Assert.Throws(() => graphics.DrawClosedCurve(pen, new Point[3], 1, FillMode.Winding)); + Assert.Throws(() => graphics.DrawClosedCurve(pen, new PointF[3])); + Assert.Throws(() => graphics.DrawClosedCurve(pen, new PointF[3], 1, FillMode.Winding)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawClosedCurve_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[3])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new Point[3], 1, FillMode.Alternate)); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[3])); + AssertExtensions.Throws(null, () => graphics.DrawClosedCurve(pen, new PointF[3], 1, FillMode.Alternate)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("brush", () => graphics.FillPie(null, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws("brush", () => graphics.FillPie(null, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws("brush", () => graphics.FillPie(null, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var brush = new SolidBrush(Color.Red); + brush.Dispose(); + + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_ZeroWidth_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new Rectangle(0, 0, 0, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0, 0, 0, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0f, 0f, 0f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_ZeroHeight_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new Rectangle(0, 0, 1, 0), 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0, 0, 1, 0, 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0f, 0f, 1f, 0f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.FillPie(brush, new Rectangle(0, 0, 1, 1), 0, 90)); + Assert.Throws(() => graphics.FillPie(brush, 0, 0, 1, 1, 0, 90)); + Assert.Throws(() => graphics.FillPie(brush, 0f, 0f, 1f, 1f, 0, 90)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FillPie_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new SolidBrush(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.FillPie(brush, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.FillPie(brush, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clear_Color_Success() + { + Color color = Color.FromArgb(Color.Plum.ToArgb()); + + using (var image = new Bitmap(2, 2)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var brush = new SolidBrush(color)) + { + graphics.FillRectangle(brush, new Rectangle(0, 0, 2, 2)); + + graphics.Clear(color); + Helpers.VerifyBitmap(image, new Color[][] + { + new Color[] { color, color }, + new Color[] { color, color } + }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clear_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.Clear(Color.Red)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clear_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.Clear(Color.Red)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawString_DefaultFont_Succeeds() + { + using (var image = new Bitmap(50, 50)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawString("Test text", SystemFonts.DefaultFont, Brushes.White, new Point()); + Helpers.VerifyBitmapNotBlank(image); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawString_CompositingModeSourceCopy_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.CompositingMode = CompositingMode.SourceCopy; + AssertExtensions.Throws( + null, + () => graphics.DrawString("Test text", SystemFonts.DefaultFont, Brushes.White, new Point())); + } + } + + private static void VerifyGraphics(Graphics graphics, RectangleF expectedVisibleClipBounds) + { + Assert.NotNull(graphics.Clip); + Assert.Equal(new RectangleF(-4194304, -4194304, 8388608, 8388608), graphics.ClipBounds); + Assert.Equal(CompositingMode.SourceOver, graphics.CompositingMode); + Assert.Equal(CompositingQuality.Default, graphics.CompositingQuality); + Assert.Equal(96, graphics.DpiX); + Assert.Equal(96, graphics.DpiY); + Assert.Equal(InterpolationMode.Bilinear, graphics.InterpolationMode); + Assert.False(graphics.IsClipEmpty); + Assert.False(graphics.IsVisibleClipEmpty); + Assert.Equal(1, graphics.PageScale); + Assert.Equal(GraphicsUnit.Display, graphics.PageUnit); + Assert.Equal(PixelOffsetMode.Default, graphics.PixelOffsetMode); + Assert.Equal(Point.Empty, graphics.RenderingOrigin); + Assert.Equal(SmoothingMode.None, graphics.SmoothingMode); + Assert.Equal(4, graphics.TextContrast); + Assert.Equal(TextRenderingHint.SystemDefault, graphics.TextRenderingHint); + Assert.Equal(new Matrix(), graphics.Transform); + Assert.Equal(expectedVisibleClipBounds, graphics.VisibleClipBounds); + } + } +} diff --git a/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs b/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs new file mode 100644 index 00000000000..8fecb6ce39e --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_DrawBezierTests.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class Graphics_DrawBezierTests : DrawingTest + { + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void DrawBezier_Point() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawBezier(pen, new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10)); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xa4, 0xb9, 0x73, 0xb9, 0x6f, 0x3a, 0x85, 0x21, 0xd3, 0x65, 0x87, 0x24, 0xcf, 0x6d, 0x61, 0x94 } + : new byte[] { 0xcf, 0x92, 0xaa, 0xe2, 0x44, 0xd4, 0xdd, 0xae, 0xdd, 0x4c, 0x8a, 0xf5, 0xc3, 0x65, 0xac, 0xf2 }); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/26624", TargetFrameworkMonikers.Netcoreapp)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBezier_Points() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.Red)) + using (Graphics graphics = Graphics.FromImage(image)) + { + Point[] points = + { + new Point(10, 10), new Point(20, 1), new Point(35, 5), new Point(50, 10), + new Point(60, 15), new Point(65, 25), new Point(50, 30) + }; + + graphics.DrawBeziers(pen, points); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xd0, 0x00, 0x08, 0x21, 0x06, 0x29, 0xd8, 0xab, 0x19, 0xc5, 0xc9, 0xf6, 0xf2, 0x69, 0x30, 0x1f } + : new byte[] { 0x9d, 0x24, 0x9f, 0x91, 0xa3, 0xa5, 0x60, 0xde, 0x14, 0x69, 0x42, 0xa8, 0xe6, 0xc6, 0xbf, 0xc9 }); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/26624", TargetFrameworkMonikers.Netcoreapp)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBezier_PointFs() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.Red)) + using (Graphics graphics = Graphics.FromImage(image)) + { + PointF[] points = + { + new PointF(10.0F, 10.0F), new PointF(20.0F, 1.0F), new PointF(35.0F, 5.0F), new PointF(50.0F, 10.0F), + new PointF(60.0F, 15.0F), new PointF(65.0F, 25.0F), new PointF(50.0F, 30.0F) + }; + + graphics.DrawBeziers(pen, points); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0xd0, 0x00, 0x08, 0x21, 0x06, 0x29, 0xd8, 0xab, 0x19, 0xc5, 0xc9, 0xf6, 0xf2, 0x69, 0x30, 0x1f } + : new byte[] { 0x9d, 0x24, 0x9f, 0x91, 0xa3, 0xa5, 0x60, 0xde, 0x14, 0x69, 0x42, 0xa8, 0xe6, 0xc6, 0xbf, 0xc9 }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBezier_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, 1, 2, 3, 4, 5, 6, 7, 8)); + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawBezier(null, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBezier_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void DrawBezier_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawBezier(pen, 1, 2, 3, 4, 5, 6, 7, 8)); + Assert.Throws(() => graphics.DrawBezier(pen, Point.Empty, Point.Empty, Point.Empty, Point.Empty)); + Assert.Throws(() => graphics.DrawBezier(pen, PointF.Empty, PointF.Empty, PointF.Empty, PointF.Empty)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void DrawBezier_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new Rectangle(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0, 0, 1, 1, 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, new RectangleF(0, 0, 1, 1), 0, 90)); + AssertExtensions.Throws(null, () => graphics.DrawArc(pen, 0f, 0f, 1f, 1f, 0, 90)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawBeziers(null, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawBeziers(pen, (PointF[])null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_EmptyPoints_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[0])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawBeziers(pen, new Point[2])); + Assert.Throws(() => graphics.DrawBeziers(pen, new PointF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawBeziers_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawBeziers(pen, new PointF[2])); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs b/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs new file mode 100644 index 00000000000..a8ec9af5194 --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_DrawLineTests.cs @@ -0,0 +1,195 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Tests +{ + public class Graphics_DrawLineTests : DrawingTest + { + [ActiveIssue("https://github.com/dotnet/runtime/issues/26624", TargetFrameworkMonikers.Netcoreapp)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_Points() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawLines(pen, new Point[] { new Point(1, 1), new Point(1, 10), new Point(20, 5), new Point(25, 30) }); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0x8e, 0xc2, 0xfb, 0xb4, 0xde, 0x5d, 0xdc, 0xd2, 0x31, 0xbd, 0xd3, 0x9a, 0xcf, 0xc1, 0xd4, 0xad } + : new byte[] { 0x55, 0x40, 0xd8, 0xaa, 0xc7, 0x36, 0x06, 0x18, 0x1a, 0x57, 0x2b, 0xa9, 0x5a, 0xff, 0x2b, 0xb2 }); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/26624", TargetFrameworkMonikers.Netcoreapp)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_PointFs() + { + using (Bitmap image = new Bitmap(100, 100)) + using (Pen pen = new Pen(Color.White)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.DrawLines(pen, new PointF[] { new PointF(1.0F, 1.0F), new PointF(1.0F, 10.0F), new PointF(20.0F, 5.0F), new PointF(25.0F, 30.0F) }); + ValidateImageContent(image, + PlatformDetection.IsWindows + ? new byte[] { 0x8e, 0xc2, 0xfb, 0xb4, 0xde, 0x5d, 0xdc, 0xd2, 0x31, 0xbd, 0xd3, 0x9a, 0xcf, 0xc1, 0xd4, 0xad } + : new byte[] { 0x55, 0x40, 0xd8, 0xaa, 0xc7, 0x36, 0x06, 0x18, 0x1a, 0x57, 0x2b, 0xa9, 0x5a, 0xff, 0x2b, 0xb2 }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLine_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, Point.Empty, Point.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0, 0, 0, 0)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws("pen", () => graphics.DrawLine(null, 0f, 0f, 0f, 0f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLine_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLine_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + Assert.Throws(() => graphics.DrawLine(pen, 0, 0, 0, 0)); + Assert.Throws(() => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + Assert.Throws(() => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLine_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, Point.Empty, Point.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0, 0, 0, 0)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, PointF.Empty, PointF.Empty)); + AssertExtensions.Throws(null, () => graphics.DrawLine(pen, 0f, 0f, 0f, 0f)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_NullPen_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new Point[2])); + AssertExtensions.Throws("pen", () => graphics.DrawLines(null, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_DisposedPen_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + var pen = new Pen(Color.Red); + pen.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_NullPoints_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (Point[])null)); + AssertExtensions.Throws("points", () => graphics.DrawLines(pen, (PointF[])null)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + public void DrawLines_InvalidPointsLength_ThrowsArgumentException(int length) + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[length])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[length])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_Busy_ThrowsInvalidOperationException() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (var pen = new Pen(Color.Red)) + { + graphics.GetHdc(); + try + { + Assert.Throws(() => graphics.DrawLines(pen, new Point[2])); + Assert.Throws(() => graphics.DrawLines(pen, new PointF[2])); + } + finally + { + graphics.ReleaseHdc(); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DrawLines_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var pen = new Pen(Color.Red)) + { + Graphics graphics = Graphics.FromImage(image); + graphics.Dispose(); + + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new Point[2])); + AssertExtensions.Throws(null, () => graphics.DrawLines(pen, new PointF[2])); + } + } + + } +} diff --git a/src/System.Drawing.Common/tests/Graphics_GetContextTests.Core.cs b/src/System.Drawing.Common/tests/Graphics_GetContextTests.Core.cs new file mode 100644 index 00000000000..0e09cef3178 --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_GetContextTests.Core.cs @@ -0,0 +1,149 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using System.Numerics; +using Xunit; + +namespace System.Drawing.Tests +{ + public partial class Graphics_GetContextTests + { + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_DefaultGraphics() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.GetContextInfo(out PointF offset); + Assert.True(offset.IsEmpty); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.True(offset.IsEmpty); + Assert.Null(clip); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_Clipping() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + + graphics.GetContextInfo(out PointF offset); + Assert.True(offset.IsEmpty); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.True(offset.IsEmpty); + Assert.NotNull(clip); + Assert.Equal(initialClip.GetBounds(graphics), clip.GetBounds(graphics)); + clip.Dispose(); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_Transform() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.TransformElements = Matrix3x2.CreateTranslation(1, 2); + + graphics.GetContextInfo(out PointF offset); + Assert.Equal(new PointF(1, 2), offset); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.Null(clip); + Assert.Equal(new PointF(1, 2), offset); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_ClipAndTransform() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + graphics.TransformElements = Matrix3x2.CreateTranslation(1, 2); + + graphics.GetContextInfo(out PointF offset); + Assert.Equal(new PointF(1, 2), offset); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.NotNull(clip); + Assert.Equal(new RectangleF(0, 0, 9, 10), clip.GetBounds(graphics)); + Assert.Equal(new PointF(1, 2), offset); + clip.Dispose(); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_TransformAndClip() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.TransformElements = Matrix3x2.CreateTranslation(1, 2); + graphics.Clip = initialClip; + + graphics.GetContextInfo(out PointF offset); + Assert.Equal(new PointF(1, 2), offset); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.NotNull(clip); + Assert.Equal(new RectangleF(1, 2, 9, 10), clip.GetBounds(graphics)); + Assert.Equal(new PointF(1, 2), offset); + clip.Dispose(); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_ClipAndTransformSaveState() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + graphics.TransformElements = Matrix3x2.CreateTranslation(1, 2); + + GraphicsState state = graphics.Save(); + + graphics.GetContextInfo(out PointF offset); + Assert.Equal(new PointF(2, 4), offset); + + graphics.GetContextInfo(out offset, out Region? clip); + Assert.NotNull(clip); + Assert.Equal(new RectangleF(0, 0, 8, 8), clip.GetBounds(graphics)); + Assert.Equal(new PointF(2, 4), offset); + clip.Dispose(); + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_New_ClipAndTransformSaveAndRestoreState() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + graphics.SetClip(new Rectangle(1, 2, 9, 10)); + graphics.TransformElements = Matrix3x2.CreateTranslation(1, 2); + + GraphicsState state = graphics.Save(); + graphics.GetContextInfo(out PointF offset, out Region? clip); + graphics.Restore(state); + + Assert.NotNull(clip); + Assert.Equal(new RectangleF(0, 0, 8, 8), clip.GetBounds(graphics)); + Assert.Equal(new PointF(2, 4), offset); + clip.Dispose(); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Graphics_GetContextTests.cs b/src/System.Drawing.Common/tests/Graphics_GetContextTests.cs new file mode 100644 index 00000000000..deb05c2a039 --- /dev/null +++ b/src/System.Drawing.Common/tests/Graphics_GetContextTests.cs @@ -0,0 +1,168 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Drawing2D; +using Xunit; + +namespace System.Drawing.Tests +{ +#pragma warning disable SYSLIB0016 // Type or member is obsolete + public partial class Graphics_GetContextTests : DrawingTest + { + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_DefaultGraphics() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + { + object info = graphics.GetContextInfo(); + Assert.IsType(info); + object[] infoArray = (object[])info; + Assert.Equal(2, infoArray.Length); + Assert.IsType(infoArray[0]); + Assert.IsType(infoArray[1]); + using (Region region = (Region)infoArray[0]) + using (Matrix matrix = (Matrix)infoArray[1]) + { + Assert.True(region.IsInfinite(graphics)); + Assert.True(matrix.IsIdentity); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_Clipping() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + + object[] info = (object[])graphics.GetContextInfo(); + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + Assert.Equal(initialClip.GetBounds(graphics), region.GetBounds(graphics)); + Assert.True(matrix.IsIdentity); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_Transform() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Matrix initialTransform = new Matrix()) + { + initialTransform.Translate(1, 2); + graphics.Transform = initialTransform; + + object[] info = (object[])graphics.GetContextInfo(); + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + Assert.True(region.IsInfinite(graphics)); + Assert.Equal(initialTransform, matrix); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_ClipAndTransform() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Matrix initialTransform = new Matrix()) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + initialTransform.Translate(1, 2); + graphics.Transform = initialTransform; + + object[] info = (object[])graphics.GetContextInfo(); + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + Assert.Equal(new RectangleF(0, 0, 9, 10), region.GetBounds(graphics)); + Assert.Equal(initialTransform, matrix); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_TransformAndClip() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Matrix initialTransform = new Matrix()) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + initialTransform.Translate(1, 2); + graphics.Transform = initialTransform; + graphics.Clip = initialClip; + + object[] info = (object[])graphics.GetContextInfo(); + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + Assert.Equal(new RectangleF(1, 2, 9, 10), region.GetBounds(graphics)); + Assert.Equal(initialTransform, matrix); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_ClipAndTransformSaveState() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Matrix initialTransform = new Matrix()) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + initialTransform.Translate(1, 2); + graphics.Transform = initialTransform; + + GraphicsState state = graphics.Save(); + object[] info = (object[])graphics.GetContextInfo(); + + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + initialTransform.Translate(1, 2); + Assert.Equal(new RectangleF(0, 0, 8, 8), region.GetBounds(graphics)); + Assert.Equal(initialTransform, matrix); + } + } + } + + [ConditionalFact(Helpers.IsWindows)] + public void GetContextInfo_ClipAndTransformSaveAndRestoreState() + { + using (var image = new Bitmap(10, 10)) + using (Graphics graphics = Graphics.FromImage(image)) + using (Matrix initialTransform = new Matrix()) + using (Region initialClip = new Region(new Rectangle(1, 2, 9, 10))) + { + graphics.Clip = initialClip; + initialTransform.Translate(1, 2); + graphics.Transform = initialTransform; + + GraphicsState state = graphics.Save(); + object[] info = (object[])graphics.GetContextInfo(); + graphics.Restore(state); + + using (Region region = (Region)info[0]) + using (Matrix matrix = (Matrix)info[1]) + { + initialTransform.Translate(1, 2); + Assert.Equal(new RectangleF(0, 0, 8, 8), region.GetBounds(graphics)); + Assert.Equal(initialTransform, matrix); + } + } + } + } +#pragma warning restore SYSLIB0016 // Type or member is obsolete +} diff --git a/src/System.Drawing.Common/tests/Helpers.cs b/src/System.Drawing.Common/tests/Helpers.cs new file mode 100644 index 00000000000..84818358e4b --- /dev/null +++ b/src/System.Drawing.Common/tests/Helpers.cs @@ -0,0 +1,233 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing.Printing; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Xunit; +using Xunit.Sdk; + +namespace System.Drawing +{ + public static class Helpers + { + public const string IsDrawingSupported = nameof(Helpers) + "." + nameof(GetIsDrawingSupported); + public const string IsWindowsOrAtLeastLibgdiplus6 = nameof(Helpers) + "." + nameof(GetIsWindowsOrAtLeastLibgdiplus6); + public const string RecentGdiplusIsAvailable = IsWindowsOrAtLeastLibgdiplus6; + public const string GdiPlusIsAvailableNotWindows7 = nameof(Helpers) + "." + nameof(GetGdiPlusIsAvailableNotWindows7); + public const string AnyInstalledPrinters = nameof(Helpers) + "." + nameof(IsAnyInstalledPrinters); + public const string WindowsRS3OrEarlier = nameof(Helpers) + "." + nameof(IsWindowsRS3OrEarlier); + public const string IsWindows = nameof(Helpers) + "." + nameof(GetIsWindows); + + public static bool GetIsDrawingSupported() => PlatformDetection.IsDrawingSupported; + + public static bool GetIsWindowsOrAtLeastLibgdiplus6() + { + if (!PlatformDetection.IsDrawingSupported) + { + return false; + } + + if (PlatformDetection.IsWindows) + { + return true; + } + + Version installedVersion; + + try + { + installedVersion = new Version(GetLibgdiplusVersion()); + } + catch (DllNotFoundException) + { + return false; + } + catch (EntryPointNotFoundException) + { + return false; + } + + return installedVersion.Major >= 6; + } + + public static bool GetIsWindows() => PlatformDetection.IsDrawingSupported && PlatformDetection.IsWindows; + + public static bool IsNotUnix => PlatformDetection.IsWindows; + + public static bool IsWindowsRS3OrEarlier => !PlatformDetection.IsWindows10Version1803OrGreater; + + public static bool GetGdiPlusIsAvailableNotWindows7() + { + if (PlatformDetection.IsWindows7) + { + return false; + } + + return GetIsDrawingSupported(); + } + + public static bool IsAnyInstalledPrinters() + { + return PrinterSettings.InstalledPrinters.Count > 0; + } + + public static string GetTestBitmapPath(string fileName) => GetTestPath("bitmaps", fileName); + public static string GetTestFontPath(string fileName) => GetTestPath("fonts", fileName); + public static string GetTestColorProfilePath(string fileName) => GetTestPath("colorProfiles", fileName); + + private static string GetTestPath(string directoryName, string fileName) => Path.Combine(AppContext.BaseDirectory, directoryName, fileName); + + public static void VerifyBitmap(Bitmap bitmap, Color[][] colors) + { + for (int y = 0; y < colors.Length; y++) + { + for (int x = 0; x < colors[y].Length; x++) + { + Color expectedColor = Color.FromArgb(colors[y][x].ToArgb()); + Color actualColor = bitmap.GetPixel(x, y); + + if (expectedColor != actualColor) + { + throw GetBitmapEqualFailureException(bitmap, colors, x, y); + } + } + } + } + + private static Exception GetBitmapEqualFailureException(Bitmap bitmap, Color[][] colors, int firstFailureX, int firstFailureY) + { + // Print out the whole bitmap to provide a view of the whole image, rather than just the difference between + // a single pixel. + var actualStringBuilder = new StringBuilder(); + var expectedStringBuilder = new StringBuilder(); + + actualStringBuilder.AppendLine(); + expectedStringBuilder.AppendLine(); + + for (int y = 0; y < bitmap.Height; y++) + { + for (int x = 0; x < bitmap.Width; x++) + { + PrintColor(actualStringBuilder, bitmap.GetPixel(x, y)); + PrintColor(expectedStringBuilder, colors[y][x]); + if (x != bitmap.Width - 1) + { + actualStringBuilder.Append(", "); + expectedStringBuilder.Append(", "); + } + } + actualStringBuilder.AppendLine(); + expectedStringBuilder.AppendLine(); + } + + return new AssertActualExpectedException(expectedStringBuilder.ToString(), actualStringBuilder.ToString(), $"Bitmaps were different at {firstFailureX}, {firstFailureY}."); + } + + private static void PrintColor(StringBuilder stringBuilder, Color color) + { + stringBuilder.Append($"Color.FromArgb({color.A}, {color.R}, {color.G}, {color.B})"); + } + + public static Color EmptyColor => Color.FromArgb(0, 0, 0, 0); + + private static Rectangle GetRectangle(RECT rect) + { + return new Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top); + } + + private const int MONITOR_DEFAULTTOPRIMARY = 1; + + [DllImport("libgdiplus", ExactSpelling = true)] + internal static extern string GetLibgdiplusVersion(); + + [DllImport("user32.dll", SetLastError = true)] + private static extern IntPtr MonitorFromWindow(IntPtr hWnd, int dwFlags); + + [DllImport("user32.dll", SetLastError = true)] + private static extern int GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO monitorInfo); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", ExactSpelling = true, SetLastError = true)] + internal static extern int GetGuiResources(IntPtr hProcess, uint flags); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetDC(IntPtr hWnd); + + [DllImport("user32.dll", SetLastError = true)] + internal static extern IntPtr GetWindowDC(IntPtr hWnd); + + public static Rectangle GetWindowDCRect(IntPtr hdc) => GetHWndRect(WindowFromDC(hdc)); + + public static Rectangle GetHWndRect(IntPtr hWnd) + { + if (hWnd == IntPtr.Zero) + { + return GetMonitorRectForWindow(hWnd); + } + + var rect = new RECT(); + GetClientRect(hWnd, ref rect); + + return GetRectangle(rect); + } + + private static Rectangle GetMonitorRectForWindow(IntPtr hWnd) + { + IntPtr hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTOPRIMARY); + Assert.NotEqual(IntPtr.Zero, hMonitor); + + var info = new MONITORINFO(); + info.cbSize = Marshal.SizeOf(info); + int result = GetMonitorInfo(hMonitor, ref info); + Assert.NotEqual(0, result); + + return GetRectangle(info.rcMonitor); + } + + [DllImport("user32.dll", SetLastError = true)] + private static extern int GetClientRect(IntPtr hWnd, ref RECT lpRect); + + [DllImport("user32.dll", SetLastError = true)] + private static extern IntPtr WindowFromDC(IntPtr hdc); + + [StructLayout(LayoutKind.Sequential)] + private struct MONITORINFO + { + public int cbSize; + public RECT rcMonitor; + public RECT rcWork; + public int dwFlags; + } + + [StructLayout(LayoutKind.Sequential)] + private struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + public static void VerifyBitmapNotBlank(Bitmap bmp) + { + Color emptyColor = Color.FromArgb(0); + for (int y = 0; y < bmp.Height; y++) + { + for (int x = 0; x < bmp.Width; x++) + { + Color pixel = bmp.GetPixel(x, y); + if (!pixel.Equals(emptyColor)) + { + return; + } + } + } + + throw new XunitException("The entire image was blank."); + } + } +} diff --git a/src/System.Drawing.Common/tests/IconTests.cs b/src/System.Drawing.Common/tests/IconTests.cs new file mode 100644 index 00000000000..249d17065b9 --- /dev/null +++ b/src/System.Drawing.Common/tests/IconTests.cs @@ -0,0 +1,867 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2004,2006-2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.DotNet.RemoteExecutor; +using Xunit; + +namespace System.Drawing.Tests +{ + public class IconTests + { + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("48x48_multiple_entries_4bit.ico")] + [InlineData("256x256_seven_entries_multiple_bits.ico")] + [InlineData("pngwithheight_icon.ico")] + public void Ctor_FilePath(string name) + { + using (var icon = new Icon(Helpers.GetTestBitmapPath(name))) + { + Assert.Equal(32, icon.Width); + Assert.Equal(32, icon.Height); + Assert.Equal(new Size(32, 32), icon.Size); + } + } + + public static IEnumerable Size_TestData() + { + // Normal size + yield return new object[] { "48x48_multiple_entries_4bit.ico", new Size(16, 16), new Size(16, 16) }; + yield return new object[] { "48x48_multiple_entries_4bit.ico", new Size(-32, -32), new Size(16, 16) }; + yield return new object[] { "48x48_multiple_entries_4bit.ico", new Size(32, 16), new Size(32, 32) }; + yield return new object[] { "256x256_seven_entries_multiple_bits.ico", new Size(48, 48), new Size(48, 48) }; + yield return new object[] { "256x256_seven_entries_multiple_bits.ico", new Size(0, 0), new Size(32, 32) }; + yield return new object[] { "256x256_seven_entries_multiple_bits.ico", new Size(1, 1), new Size(256, 256) }; + + // Unusual size + yield return new object[] { "10x16_one_entry_32bit.ico", new Size(16, 16), new Size(10, 16) }; + yield return new object[] { "10x16_one_entry_32bit.ico", new Size(32, 32), new Size(11, 22) }; + + // Only 256 + yield return new object[] { "256x256_one_entry_32bit.ico", new Size(0, 0), new Size(256, 256) }; + + yield return new object[] { "256x256_one_entry_32bit.ico", new Size(int.MaxValue, int.MaxValue), new Size(256, 256) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_FilePath_Width_Height(string fileName, Size size, Size expectedSize) + { + using (var icon = new Icon(Helpers.GetTestBitmapPath(fileName), size.Width, size.Height)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_FilePath_Size(string fileName, Size size, Size expectedSize) + { + using (var icon = new Icon(Helpers.GetTestBitmapPath(fileName), size)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullFilePath_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => new Icon((string)null)); + AssertExtensions.Throws("path", () => new Icon((string)null, new Size(32, 32))); + AssertExtensions.Throws("path", () => new Icon((string)null, 32, 32)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Stream() + { + using (var stream = File.OpenRead(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + { + var icon = new Icon(stream); + Assert.Equal(32, icon.Width); + Assert.Equal(32, icon.Height); + Assert.Equal(new Size(32, 32), icon.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Bug fix in core")] + public void Ctor_Stream_Trickled() + { + var stream = new TrickleStream(File.ReadAllBytes(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))); + var icon = new Icon(stream); + Assert.Equal(32, icon.Width); + Assert.Equal(32, icon.Height); + Assert.Equal(new Size(32, 32), icon.Size); + } + + private sealed class TrickleStream : MemoryStream + { + public TrickleStream(byte[] bytes) : base(bytes) { } + public override int Read(byte[] buffer, int offset, int count) => base.Read(buffer, offset, Math.Min(count, 1)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_Stream_Width_Height(string fileName, Size size, Size expectedSize) + { + using (var stream = File.OpenRead(Helpers.GetTestBitmapPath(fileName))) + using (var icon = new Icon(stream, size.Width, size.Height)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_Stream_Size(string fileName, Size size, Size expectedSize) + { + using (var stream = File.OpenRead(Helpers.GetTestBitmapPath(fileName))) + using (var icon = new Icon(stream, size)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullStream_ThrowsArgumentNullException() + { + AssertExtensions.Throws("stream", null, () => new Icon((Stream)null)); + AssertExtensions.Throws("stream", null, () => new Icon((Stream)null, 32, 32)); + AssertExtensions.Throws("stream", null, () => new Icon((Stream)null, new Size(32, 32))); + } + + public static IEnumerable Ctor_InvalidBytesInStream_TestData() + { + // No start entry. + yield return new object[] { new byte[0], typeof(ArgumentException) }; + yield return new object[] { new byte[6], typeof(ArgumentException) }; + yield return new object[] { new byte[21], typeof(ArgumentException) }; + + // First two reserved bits are not zero. + yield return new object[] { new byte[] { 10, 0, 1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + yield return new object[] { new byte[] { 0, 10, 1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + + // The type is not one. + yield return new object[] { new byte[] { 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + yield return new object[] { new byte[] { 0, 0, 2, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + yield return new object[] { new byte[] { 0, 0, 1, 2, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + + // The count is zero. + yield return new object[] { new byte[] { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + + // No space for the number of entries specified. + yield return new object[] { new byte[] { 0, 0, 1, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(ArgumentException) }; + + // The number of entries specified is negative. + yield return new object[] + { + new byte[] { 0, 0, 1, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + + // There is no such thing as a negative number in the native struct, we're throwing ArgumentException + // here now as the data size doesn't match what is expected (as other inputs above). + PlatformDetection.IsNetFramework ? typeof(Win32Exception) : typeof(ArgumentException) + }; + + // The size of an entry is negative. + yield return new object[] { new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0 }, typeof(Win32Exception) }; + + // The offset of an entry is negative. + yield return new object[] { new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255 }, typeof(ArgumentException) }; + + // The size and offset of an entry refers to an invalid position in the list of entries. + yield return new object[] { new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0 }, typeof(ArgumentException) }; + + // The size and offset of an entry overflows. + yield return new object[] + { + new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 127, 255, 255, 255, 127 }, + + // Another case where we weren't checking data integrity before invoking. + PlatformDetection.IsNetFramework ? typeof(Win32Exception) : typeof(ArgumentException) + }; + + // The offset and the size of the list of entries overflows. + yield return new object[] { new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 127 }, typeof(ArgumentException) }; + + // No handle can be created from this. + yield return new object[] { new byte[] { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, typeof(Win32Exception) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_InvalidBytesInStream_TestData))] + public void Ctor_InvalidBytesInStream_ThrowsException(byte[] bytes, Type exceptionType) + { + using (var stream = new MemoryStream()) + { + stream.Write(bytes, 0, bytes.Length); + + stream.Position = 0; + Assert.Throws(exceptionType, () => new Icon(stream)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_Icon_Width_Height(string fileName, Size size, Size expectedSize) + { + using (var sourceIcon = new Icon(Helpers.GetTestBitmapPath(fileName))) + using (var icon = new Icon(sourceIcon, size.Width, size.Height)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + Assert.NotEqual(sourceIcon.Handle, icon.Handle); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Size_TestData))] + public void Ctor_Icon_Size(string fileName, Size size, Size expectedSize) + { + using (var sourceIcon = new Icon(Helpers.GetTestBitmapPath(fileName))) + using (var icon = new Icon(sourceIcon, size)) + { + Assert.Equal(expectedSize.Width, icon.Width); + Assert.Equal(expectedSize.Height, icon.Height); + Assert.Equal(expectedSize, icon.Size); + Assert.NotEqual(sourceIcon.Handle, icon.Handle); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullIcon_ThrowsArgumentNullException() + { + AssertExtensions.Throws("original", null, () => new Icon((Icon)null, 32, 32)); + AssertExtensions.Throws("original", null, () => new Icon((Icon)null, new Size(32, 32))); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Type_Resource() + { + using (var icon = new Icon(typeof(IconTests), "48x48_multiple_entries_4bit.ico")) + { + Assert.Equal(32, icon.Height); + Assert.Equal(32, icon.Width); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullType_ThrowsNullReferenceException() + { + Assert.Throws(() => new Icon(null, "48x48_multiple_entries_4bit.ico")); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(typeof(Icon), "")] + [InlineData(typeof(Icon), "48x48_multiple_entries_4bit.ico")] + [InlineData(typeof(IconTests), "48x48_MULTIPLE_entries_4bit.ico")] + public void Ctor_InvalidResource_ThrowsArgumentException(Type type, string resource) + { + AssertExtensions.Throws(null, () => new Icon(type, resource)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_InvalidResource_ThrowsArgumentNullException() + { + AssertExtensions.Throws("resource", null, () => new Icon(typeof(Icon), null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_ConstructedIcon_Success() + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + using (Icon clone = Assert.IsType(icon.Clone())) + { + Assert.NotSame(icon, clone); + Assert.NotEqual(icon.Handle, clone.Handle); + Assert.Equal(32, clone.Width); + Assert.Equal(32, clone.Height); + Assert.Equal(new Size(32, 32), clone.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_IconFromHandle_Success() + { + using (var icon = Icon.FromHandle(SystemIcons.Hand.Handle)) + using (Icon clone = Assert.IsType(icon.Clone())) + { + Assert.NotSame(icon, clone); + Assert.NotEqual(icon.Handle, clone.Handle); + Assert.Equal(SystemIcons.Hand.Width, clone.Width); + Assert.Equal(SystemIcons.Hand.Height, clone.Height); + Assert.Equal(SystemIcons.Hand.Size, clone.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_IconData_DestroysHandle() + { + var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico")); + icon.Dispose(); + Assert.Throws(() => icon.Handle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_OwnsHandle_DestroysHandle() + { + Icon icon = Icon.ExtractAssociatedIcon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico")); + icon.Dispose(); + + Assert.Throws(() => icon.Handle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_DoesNotOwnHandle_DoesNotDestroyHandle() + { + using (var source = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + using (var icon = Icon.FromHandle(source.Handle)) + { + IntPtr handle = icon.Handle; + Assert.NotEqual(IntPtr.Zero, handle); + + icon.Dispose(); + Assert.Equal(handle, icon.Handle); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(16)] + [InlineData(32)] + [InlineData(48)] + public void XpIcon_ToBitmap_Success(int size) + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_32bit.ico"), size, size)) + { + Assert.Equal(size, icon.Width); + Assert.Equal(size, icon.Height); + Assert.Equal(new Size(size, size), icon.Size); + + using (Bitmap bitmap = icon.ToBitmap()) + { + Assert.Equal(size, bitmap.Width); + Assert.Equal(size, bitmap.Height); + Assert.Equal(new Size(size, size), bitmap.Size); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_FilePath_Success() + { + ExtractAssociatedIcon_FilePath_Success_Helper(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico")); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Fix for https://github.com/dotnet/runtime/issues/28220")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_UNCFilePath_Success() + { + string bitmapPath = Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"); + string bitmapPathRoot = Path.GetPathRoot(bitmapPath); + string bitmapUncPath = $"\\\\{Environment.MachineName}\\{bitmapPath.Substring(0, bitmapPathRoot.IndexOf(":"))}$\\{bitmapPath.Replace(bitmapPathRoot, "")}"; + + // Some path could not be accessible + // if so we just pass the test + try + { + File.Open(bitmapUncPath, FileMode.Open, FileAccess.Read, FileShare.Read).Dispose(); + } + catch (IOException) + { + return; + } + + Assert.True(new Uri(bitmapUncPath).IsUnc); + + ExtractAssociatedIcon_FilePath_Success_Helper(bitmapUncPath); + } + + private void ExtractAssociatedIcon_FilePath_Success_Helper(string filePath) + { + using (Icon icon = Icon.ExtractAssociatedIcon(filePath)) + { + Assert.Equal(32, icon.Width); + Assert.Equal(32, icon.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_NonFilePath_ThrowsFileNotFound() + { + // Used to return null at the expense of creating a URI + if (PlatformDetection.IsNetFramework) + { + Assert.Null(Icon.ExtractAssociatedIcon("http://microsoft.com")); + } + else + { + Assert.Throws(() => Icon.ExtractAssociatedIcon("http://microsoft.com")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_NullFilePath_ThrowsArgumentNullException() + { + AssertExtensions.Throws("filePath", null, () => Icon.ExtractAssociatedIcon(null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_InvalidFilePath_ThrowsArgumentException() + { + AssertExtensions.Throws("filePath", null, () => Icon.ExtractAssociatedIcon("")); + } + + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExtractAssociatedIcon_NoSuchPath_ThrowsFileNotFoundException() + { + Assert.Throws(() => Icon.ExtractAssociatedIcon("no-such-file.png")); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("16x16_one_entry_4bit.ico")] + [InlineData("32x32_one_entry_4bit.ico")] + [InlineData("48x48_one_entry_1bit.ico")] + [InlineData("64x64_one_entry_8bit.ico")] + [InlineData("96x96_one_entry_8bit.ico")] + [InlineData("256x256_seven_entries_multiple_bits.ico")] + public void Save_OutputStream_Success(string fileName) + { + SaveAndCompare(new Icon(Helpers.GetTestBitmapPath(fileName)), true); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_OutputStream_ProducesIdenticalBytes() + { + string filePath = Helpers.GetTestBitmapPath("256x256_seven_entries_multiple_bits.ico"); + using (var icon = new Icon(filePath)) + using (var outputStream = new MemoryStream()) + { + icon.Save(outputStream); + Assert.Equal(File.ReadAllBytes(filePath), outputStream.ToArray()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_HasIconDataAndDisposed_ProducesIdenticalBytes() + { + string filePath = Helpers.GetTestBitmapPath("256x256_seven_entries_multiple_bits.ico"); + var icon = new Icon(filePath); + icon.Dispose(); + using (var outputStream = new MemoryStream()) + { + icon.Save(outputStream); + Assert.Equal(File.ReadAllBytes(filePath), outputStream.ToArray()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_NullOutputStreamIconData_ThrowsNullReferenceException() + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + { + Assert.Throws(() => icon.Save(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Save_NullOutputStreamNoIconData_ThrowsArgumentNullException() + { + using (var source = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + { + var icon = Icon.FromHandle(source.Handle); + icon.Dispose(); + + AssertExtensions.Throws("outputStream", "dataStream", () => icon.Save(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_ClosedOutputStreamIconData_ThrowsException() + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + { + var stream = new MemoryStream(); + stream.Close(); + + Assert.Throws(() => icon.Save(stream)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_ClosedOutputStreamNoIconData() + { + using (var source = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"))) + using (var icon = Icon.FromHandle(source.Handle)) + { + var stream = new MemoryStream(); + stream.Close(); + + if (PlatformDetection.IsNetFramework) + { + // The ObjectDisposedException is ignored in previous .NET versions, + // so the following does nothing. + icon.Save(stream); + } + else + { + Assert.Throws(() => icon.Save(stream)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Save_NoIconDataOwnsHandleAndDisposed_ThrowsObjectDisposedException() + { + Icon icon = Icon.ExtractAssociatedIcon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico")); + icon.Dispose(); + + Assert.Throws(() => icon.Save(new MemoryStream())); + } + + public static IEnumerable ToBitmap_TestData() + { + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico")) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("32x32_one_entry_4bit.ico")) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico")) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico")) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico")) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("256x256_two_entries_multiple_bits.ico"), 48, 48) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("256x256_two_entries_multiple_bits.ico"), 256, 256) }; + yield return new object[] { new Icon(Helpers.GetTestBitmapPath("256x256_two_entries_multiple_bits.ico"), 0, 0) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ToBitmap_TestData))] + public void ToBitmap_BitmapIcon_ReturnsExpected(Icon icon) + { + try + { + using (Bitmap bitmap = icon.ToBitmap()) + { + Assert.NotSame(icon.ToBitmap(), bitmap); + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Empty(bitmap.Palette.Entries); + Assert.Equal(icon.Width, bitmap.Width); + Assert.Equal(icon.Height, bitmap.Height); + + Assert.Equal(ImageFormat.MemoryBmp, bitmap.RawFormat); + Assert.Equal(2, bitmap.Flags); + } + } + finally + { + icon.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToBitmap_BitmapIconFromHandle_ReturnsExpected() + { + // Handle refers to an icon without any colour. This is not in ToBitmap_TestData as there is + // a chance that the original icon will be finalized as it is not kept alive in the iterator. + using (var originalIcon = new Icon(Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"))) + using (Icon icon = Icon.FromHandle(originalIcon.Handle)) + { + ToBitmap_BitmapIcon_ReturnsExpected(icon); + } + } + + private const string DontSupportPngFramesInIcons = "Switch.System.Drawing.DontSupportPngFramesInIcons"; + + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToBitmap_PngIconSupportedInSwitches_Success() + { + void VerifyPng() + { + using (Icon icon = GetPngIcon()) + using (Bitmap bitmap = icon.ToBitmap()) + { + using (Bitmap secondBitmap = icon.ToBitmap()) + { + Assert.NotSame(icon.ToBitmap(), bitmap); + } + Assert.Equal(PixelFormat.Format32bppArgb, bitmap.PixelFormat); + Assert.Empty(bitmap.Palette.Entries); + Assert.Equal(icon.Width, bitmap.Width); + Assert.Equal(icon.Height, bitmap.Height); + + Assert.Equal(ImageFormat.Png, bitmap.RawFormat); + Assert.Equal(77842, bitmap.Flags); + } + } + + if (RemoteExecutor.IsSupported && (!AppContext.TryGetSwitch(DontSupportPngFramesInIcons, out bool isEnabled) || isEnabled)) + { + RemoteExecutor.Invoke(() => + { + AppContext.SetSwitch(DontSupportPngFramesInIcons, false); + VerifyPng(); + }).Dispose(); + } + else + { + VerifyPng(); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToBitmap_PngIconNotSupportedInSwitches_ThrowsArgumentOutOfRangeException() + { + void VerifyPngNotSupported() + { + using (Icon icon = GetPngIcon()) + { + AssertExtensions.Throws(null, () => icon.ToBitmap()); + } + } + + if (RemoteExecutor.IsSupported && (!AppContext.TryGetSwitch(DontSupportPngFramesInIcons, out bool isEnabled) || !isEnabled)) + { + RemoteExecutor.Invoke(() => + { + AppContext.SetSwitch(DontSupportPngFramesInIcons, true); + VerifyPngNotSupported(); + }).Dispose(); + } + else + { + if (AppContext.TryGetSwitch(DontSupportPngFramesInIcons, out bool enabled) && enabled) + VerifyPngNotSupported(); + } + } + + private static Icon GetPngIcon() + { + using (var stream = new MemoryStream()) + { + // Create a PNG inside an ICO. + using (var bitmap = new Bitmap(10, 10)) + { + stream.Write(new byte[] { 0, 0, 1, 0, 1, 0, (byte)bitmap.Width, (byte)bitmap.Height, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 }, 0, 22); + + // Writing actual data + bitmap.Save(stream, ImageFormat.Png); + } + + // Getting data length (file length minus header) + long length = stream.Length - 22; + stream.Seek(14, SeekOrigin.Begin); + stream.WriteByte((byte)length); + stream.WriteByte((byte)(length >> 8)); + + // Read the PNG inside an ICO. + stream.Position = 0; + return new Icon(stream); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void FromHandle_IconHandleOneTime_Success() + { + using (var icon1 = new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + using (Icon icon2 = Icon.FromHandle(icon1.Handle)) + { + Assert.Equal(icon1.Handle, icon2.Handle); + Assert.Equal(icon1.Size, icon2.Size); + SaveAndCompare(icon2, false); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void FromHandle_IconHandleMultipleTime_Success() + { + using (var icon1 = new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + { + using (Icon icon2 = Icon.FromHandle(icon1.Handle)) + { + Assert.Equal(icon1.Handle, icon2.Handle); + Assert.Equal(icon1.Size, icon2.Size); + SaveAndCompare(icon2, false); + } + using (Icon icon3 = Icon.FromHandle(icon1.Handle)) + { + Assert.Equal(icon1.Handle, icon3.Handle); + Assert.Equal(icon1.Size, icon3.Size); + SaveAndCompare(icon3, false); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void FromHandle_BitmapHandleOneTime_Success() + { + IntPtr handle; + using (var icon1 = new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + { + handle = icon1.ToBitmap().GetHicon(); + } + using (Icon icon2 = Icon.FromHandle(handle)) + { + Assert.Equal(handle, icon2.Handle); + Assert.Equal(new Size(16, 16), icon2.Size); + SaveAndCompare(icon2, false); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/47759", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void FromHandle_BitmapHandleMultipleTime_Success() + { + IntPtr handle; + using (var icon1 = new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + { + handle = icon1.ToBitmap().GetHicon(); + } + using (Icon icon2 = Icon.FromHandle(handle)) + { + Assert.Equal(handle, icon2.Handle); + Assert.Equal(new Size(16, 16), icon2.Size); + SaveAndCompare(icon2, false); + } + using (Icon icon3 = Icon.FromHandle(handle)) + { + Assert.Equal(handle, icon3.Handle); + Assert.Equal(new Size(16, 16), icon3.Size); + SaveAndCompare(icon3, false); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHandle_Zero_ThrowsArgumentException() + { + AssertExtensions.Throws("handle", null, () => Icon.FromHandle(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Size_GetWhenDisposed_ThrowsObjectDisposedException() + { + var icon = new Icon(Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico")); + icon.Dispose(); + + Assert.Throws(() => icon.Width); + Assert.Throws(() => icon.Height); + Assert.Throws(() => icon.Size); + } + + private static void SaveAndCompare(Icon icon, bool alpha) + { + using (MemoryStream outputStream = new MemoryStream()) + { + icon.Save(outputStream); + outputStream.Position = 0; + + using (Icon loaded = new Icon(outputStream)) + { + Assert.Equal(icon.Height, loaded.Height); + Assert.Equal(icon.Width, loaded.Width); + + using (Bitmap expected = icon.ToBitmap()) + using (Bitmap actual = loaded.ToBitmap()) + { + Assert.Equal(expected.Height, actual.Height); + Assert.Equal(expected.Width, actual.Width); + + for (int y = 0; y < expected.Height; y++) + { + for (int x = 0; x < expected.Width; x++) + { + Color e = expected.GetPixel(x, y); + Color a = actual.GetPixel(x, y); + if (alpha) + { + Assert.Equal(e.A, a.A); + } + Assert.Equal(e.R, a.R); + Assert.Equal(e.G, a.G); + Assert.Equal(e.B, a.B); + } + } + } + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CorrectColorDepthExtracted() + { + using (var stream = File.OpenRead(Helpers.GetTestBitmapPath("pngwithheight_icon.ico"))) + { + using (var icon = new Icon(stream, new Size(32, 32))) + { + // The first 32x32 icon isn't 32 bit. Checking a few pixels that are in the 32 bit entry. + using (Bitmap bitmap = icon.ToBitmap()) + { + Assert.Equal(new Size(32, 32), bitmap.Size); + + int expectedBitDepth; + string fieldName = PlatformDetection.IsNetFramework ? "bitDepth" : "s_bitDepth"; + FieldInfo fi = typeof(Icon).GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic); + expectedBitDepth = (int)fi.GetValue(null); + + // If the first icon entry was picked, the color would be black: 0xFF000000? + + switch (expectedBitDepth) + { + case 32: + Assert.Equal(0x879EE532u, (uint)bitmap.GetPixel(0, 0).ToArgb()); + Assert.Equal(0x661CD8B7u, (uint)bitmap.GetPixel(0, 31).ToArgb()); + break; + case 16: + case 8: + // There is no 16 bit 32x32 icon in this file, 8 will be picked + // as the closest match. + Assert.Equal(0x00000000u, (uint)bitmap.GetPixel(0, 0).ToArgb()); + Assert.Equal(0xFF000000u, (uint)bitmap.GetPixel(0, 31).ToArgb()); + break; + default: + Assert.False(true, $"Unexpected bitmap depth: {expectedBitDepth}"); + break; + } + } + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/ImageTests.cs b/src/System.Drawing.Common/tests/ImageTests.cs new file mode 100644 index 00000000000..e1b8512835f --- /dev/null +++ b/src/System.Drawing.Common/tests/ImageTests.cs @@ -0,0 +1,686 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; +using Encoder = System.Drawing.Imaging.Encoder; + +namespace System.Drawing.Tests +{ + [ConditionalClass(typeof(PlatformDetection),nameof(PlatformDetection.IsDrawingSupported))] + public class ImageTests + { + private const int PropertyTagLuminanceTable = 0x5090; + private const int PropertyTagChrominanceTable = 0x5091; + private const int PropertyTagExifUserComment = 0x9286; + private const int PropertyTagTypeASCII = 2; + private const int PropertyTagTypeShort = 3; + + [Fact] + public void PropertyIdList_GetBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + Assert.NotSame(bitmap.PropertyIdList, bitmap.PropertyIdList); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new int[0] in .NET Framework.")] + public void PropertyIdList_GetEmptyMemoryBitmap_ReturnsExpected() + { + using var bitmap = new Bitmap(1, 1); + Assert.Empty(bitmap.PropertyIdList); + Assert.Same(bitmap.PropertyIdList, bitmap.PropertyIdList); + } + + [Fact] + public void PropertyItems_GetBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Equal(3, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(29, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("LEAD Technologies Inc. V1.01\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + + Assert.NotSame(items, bitmap.PropertyItems); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new PropertyItem[0] in .NET Framework.")] + public void PropertyItems_GetEmptyBitmapBmp_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + Assert.Empty(bitmap.PropertyItems); + Assert.Same(bitmap.PropertyItems, bitmap.PropertyItems); + } + + [Fact] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Returns new PropertyItem[0] in .NET Framework.")] + public void PropertyItems_GetEmptyMemoryBitmap_ReturnsExpected() + { + using var bitmap = new Bitmap(1, 1); + Assert.Empty(bitmap.PropertyItems); + Assert.Same(bitmap.PropertyItems, bitmap.PropertyItems); + } + + [Fact] + public void GetPropertyItem_InvokeExistsBitmapBmp_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagExifUserComment); + Assert.Equal(PropertyTagExifUserComment, item.Id); + Assert.Equal(29, item.Len); + Assert.Equal(PropertyTagTypeASCII, item.Type); + Assert.Equal("LEAD Technologies Inc. V1.01\0", Encoding.ASCII.GetString(item.Value)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void GetPropertyItem_NoSuchPropertyItemEmptyMemoryBitmap_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(1, 1); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(propid)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void GetPropertyItem_NoSuchPropertyItemEmptyImageBitmapBmp_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(propid)); + } + + [Fact] + public void RemovePropertyItem_InvokeMemoryBitmap_Success() + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item1 = source.GetPropertyItem(PropertyTagExifUserComment); + PropertyItem item2 = source.GetPropertyItem(PropertyTagChrominanceTable); + PropertyItem item3 = source.GetPropertyItem(PropertyTagLuminanceTable); + using var bitmap = new Bitmap(1, 1); + bitmap.SetPropertyItem(item1); + bitmap.SetPropertyItem(item2); + bitmap.SetPropertyItem(item3); + + bitmap.RemovePropertyItem(PropertyTagExifUserComment); + Assert.Equal(new int[] { PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagExifUserComment)); + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagExifUserComment)); + + bitmap.RemovePropertyItem(PropertyTagLuminanceTable); + Assert.Equal(new int[] { PropertyTagChrominanceTable }, bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagLuminanceTable)); + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagLuminanceTable)); + + bitmap.RemovePropertyItem(PropertyTagChrominanceTable); + Assert.Empty(bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagChrominanceTable)); + Assert.Throws(() => bitmap.RemovePropertyItem(PropertyTagChrominanceTable)); + } + + [Fact] + public void RemovePropertyItem_InvokeBitmapJpg_Success() + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + bitmap.RemovePropertyItem(PropertyTagExifUserComment); + Assert.Equal(new int[] { PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagExifUserComment)); + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagExifUserComment)); + + bitmap.RemovePropertyItem(PropertyTagLuminanceTable); + Assert.Equal(new int[] { PropertyTagChrominanceTable }, bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagLuminanceTable)); + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(PropertyTagLuminanceTable)); + + bitmap.RemovePropertyItem(PropertyTagChrominanceTable); + Assert.Empty(bitmap.PropertyIdList); + AssertExtensions.Throws(null, () => bitmap.GetPropertyItem(PropertyTagChrominanceTable)); + Assert.Throws(() => bitmap.RemovePropertyItem(PropertyTagChrominanceTable)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyItemEmptyMemoryBitmap_ThrowsExternalException(int propid) + { + using var bitmap = new Bitmap(1, 1); + Assert.Throws(() => bitmap.RemovePropertyItem(propid)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyItemEmptyImageBitmapBmp_ThrowsExternalException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + Assert.Throws(() => bitmap.RemovePropertyItem(propid)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyNotEmptyMemoryBitmap_ThrowsArgumentException(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item1 = source.GetPropertyItem(PropertyTagExifUserComment); + PropertyItem item2 = source.GetPropertyItem(PropertyTagChrominanceTable); + PropertyItem item3 = source.GetPropertyItem(PropertyTagLuminanceTable); + using var bitmap = new Bitmap(1, 1); + bitmap.SetPropertyItem(item1); + bitmap.SetPropertyItem(item2); + bitmap.SetPropertyItem(item3); + + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(propid)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void RemovePropertyItem_NoSuchPropertyNotEmptyBitmapJpg_ThrowsArgumentException(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + AssertExtensions.Throws(null, () => bitmap.RemovePropertyItem(propid)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeMemoryBitmap_Success(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + using var bitmap = new Bitmap(1, 1); + + // Change data. + PropertyItem item = source.GetPropertyItem(PropertyTagExifUserComment); + item.Value = "Hello World\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Single(items); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + + // New data. + item.Id = propid; + item.Value = "New Value\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeBitmapJpg_Success(int propid) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + + // Change data. + PropertyItem item = bitmap.GetPropertyItem(PropertyTagExifUserComment); + item.Value = "Hello World\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Equal(3, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + + // New data. + item.Id = propid; + item.Value = "New Value\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(4, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + Assert.Equal(propid, items[3].Id); + Assert.Equal(10, items[3].Len); + Assert.Equal(PropertyTagTypeASCII, items[3].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[3].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, PropertyTagChrominanceTable, PropertyTagLuminanceTable, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(4, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(PropertyTagChrominanceTable, items[1].Id); + Assert.Equal(128, items[1].Len); + Assert.Equal(PropertyTagTypeShort, items[1].Type); + Assert.Equal(new byte[] + { + 0x16, 0x00, 0x17, 0x00, 0x1F, 0x00, 0x3E, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x17, + 0x00, 0x1B, 0x00, 0x22, 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x1F, + 0x00, 0x22, 0x00, 0x49, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x3E, + 0x00, 0x57, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, + 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00 + }, items[1].Value); + Assert.Equal(PropertyTagLuminanceTable, items[2].Id); + Assert.Equal(128, items[2].Len); + Assert.Equal(PropertyTagTypeShort, items[2].Type); + Assert.Equal(new byte[] + { + 0x15, 0x00, 0x0E, 0x00, 0x0D, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x43, 0x00, 0x50, 0x00, 0x0F, + 0x00, 0x0F, 0x00, 0x12, 0x00, 0x19, 0x00, 0x22, 0x00, 0x4C, 0x00, 0x4F, 0x00, 0x48, 0x00, 0x12, + 0x00, 0x11, 0x00, 0x15, 0x00, 0x1F, 0x00, 0x34, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x49, 0x00, 0x12, + 0x00, 0x16, 0x00, 0x1D, 0x00, 0x26, 0x00, 0x43, 0x00, 0x72, 0x00, 0x69, 0x00, 0x51, 0x00, 0x17, + 0x00, 0x1D, 0x00, 0x30, 0x00, 0x49, 0x00, 0x59, 0x00, 0x8F, 0x00, 0x87, 0x00, 0x65, 0x00, 0x1F, + 0x00, 0x2E, 0x00, 0x48, 0x00, 0x54, 0x00, 0x6A, 0x00, 0x89, 0x00, 0x95, 0x00, 0x79, 0x00, 0x40, + 0x00, 0x54, 0x00, 0x66, 0x00, 0x72, 0x00, 0x87, 0x00, 0x9F, 0x00, 0x9E, 0x00, 0x85, 0x00, 0x5F, + 0x00, 0x79, 0x00, 0x7D, 0x00, 0x81, 0x00, 0x93, 0x00, 0x84, 0x00, 0x87, 0x00, 0x82, 0x00, + }, items[2].Value); + Assert.Equal(propid, items[3].Id); + Assert.Equal(10, items[3].Len); + Assert.Equal(PropertyTagTypeASCII, items[3].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[3].Value)); + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(-1)] + public void SetPropertyItem_InvokeBitmapBmp_Success(int propid) + { + using var source = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("almogaver1bit.bmp")); + + // Change data. + PropertyItem item = source.GetPropertyItem(PropertyTagExifUserComment); + item.Value = "Hello World\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment }, bitmap.PropertyIdList); + PropertyItem[] items = bitmap.PropertyItems; + Assert.Single(items); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + + // New data. + item.Id = propid; + item.Value = "New Value\0"u8.ToArray(); + item.Len = item.Value.Length; + + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + + // Set same. + bitmap.SetPropertyItem(item); + + Assert.Equal(new int[] { PropertyTagExifUserComment, propid }, bitmap.PropertyIdList); + items = bitmap.PropertyItems; + Assert.Equal(2, items.Length); + Assert.Equal(PropertyTagExifUserComment, items[0].Id); + Assert.Equal(12, items[0].Len); + Assert.Equal(PropertyTagTypeASCII, items[0].Type); + Assert.Equal("Hello World\0", Encoding.ASCII.GetString(items[0].Value)); + Assert.Equal(propid, items[1].Id); + Assert.Equal(10, items[1].Len); + Assert.Equal(PropertyTagTypeASCII, items[1].Type); + Assert.Equal("New Value\0", Encoding.ASCII.GetString(items[1].Value)); + } + + public static IEnumerable InvalidBytes_TestData() + { + // IconTests.Ctor_InvalidBytesInStream_TestData an array of 2 objects, but this test only uses the + // 1st object. + foreach (object[] data in IconTests.Ctor_InvalidBytesInStream_TestData()) + { + yield return new object[] { data[0] }; + } + } + + [Theory] + [MemberData(nameof(InvalidBytes_TestData))] + public void FromFile_InvalidBytes_ThrowsOutOfMemoryException(byte[] bytes) + { + using (var file = TempFile.Create(bytes)) + { + Assert.Throws(() => Image.FromFile(file.Path)); + Assert.Throws(() => Image.FromFile(file.Path, useEmbeddedColorManagement: true)); + } + } + + [Fact] + public void FromFile_NullFileName_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => Image.FromFile(null)); + AssertExtensions.Throws("path", () => Image.FromFile(null, useEmbeddedColorManagement: true)); + } + + [Fact] + public void FromFile_EmptyFileName_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", null, () => Image.FromFile(string.Empty)); + AssertExtensions.Throws("path", null, () => Image.FromFile(string.Empty, useEmbeddedColorManagement: true)); + } + + [Fact] + public void FromFile_LongSegment_ThrowsException() + { + // Throws PathTooLongException on Desktop and FileNotFoundException elsewhere. + if (PlatformDetection.IsNetFramework) + { + string fileName = new string('a', 261); + + Assert.Throws(() => Image.FromFile(fileName)); + Assert.Throws(() => Image.FromFile(fileName, + useEmbeddedColorManagement: true)); + } + else + { + string fileName = new string('a', 261); + + Assert.Throws(() => Image.FromFile(fileName)); + Assert.Throws(() => Image.FromFile(fileName, + useEmbeddedColorManagement: true)); + } + } + + [Fact] + public void FromFile_NoSuchFile_ThrowsFileNotFoundException() + { + Assert.Throws(() => Image.FromFile("NoSuchFile")); + Assert.Throws(() => Image.FromFile("NoSuchFile", useEmbeddedColorManagement: true)); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [Theory] + [MemberData(nameof(InvalidBytes_TestData))] + public void FromStream_InvalidBytes_ThrowsArgumentException(byte[] bytes) + { + using (var stream = new MemoryStream()) + { + stream.Write(bytes, 0, bytes.Length); + stream.Position = 0; + + AssertExtensions.Throws(null, () => Image.FromStream(stream)); + Assert.Equal(0, stream.Position); + + AssertExtensions.Throws(null, () => Image.FromStream(stream, useEmbeddedColorManagement: true)); + AssertExtensions.Throws(null, () => Image.FromStream(stream, useEmbeddedColorManagement: true, validateImageData: true)); + Assert.Equal(0, stream.Position); + } + } + + [Fact] + public void FromStream_NullStream_ThrowsArgumentNullException() + { + AssertExtensions.Throws("stream", null, () => Image.FromStream(null)); + AssertExtensions.Throws("stream", null, () => Image.FromStream(null, useEmbeddedColorManagement: true)); + AssertExtensions.Throws("stream", null, () => Image.FromStream(null, useEmbeddedColorManagement: true, validateImageData: true)); + } + + [Theory] + [InlineData(PixelFormat.Format1bppIndexed, 1)] + [InlineData(PixelFormat.Format4bppIndexed, 4)] + [InlineData(PixelFormat.Format8bppIndexed, 8)] + [InlineData(PixelFormat.Format16bppArgb1555, 16)] + [InlineData(PixelFormat.Format16bppGrayScale, 16)] + [InlineData(PixelFormat.Format16bppRgb555, 16)] + [InlineData(PixelFormat.Format16bppRgb565, 16)] + [InlineData(PixelFormat.Format24bppRgb, 24)] + [InlineData(PixelFormat.Format32bppArgb, 32)] + [InlineData(PixelFormat.Format32bppPArgb, 32)] + [InlineData(PixelFormat.Format32bppRgb, 32)] + [InlineData(PixelFormat.Format48bppRgb, 48)] + [InlineData(PixelFormat.Format64bppArgb, 64)] + [InlineData(PixelFormat.Format64bppPArgb, 64)] + public void GetPixelFormatSize_ReturnsExpected(PixelFormat format, int expectedSize) + { + Assert.Equal(expectedSize, Image.GetPixelFormatSize(format)); + } + + [Theory] + [InlineData(PixelFormat.Format16bppArgb1555, true)] + [InlineData(PixelFormat.Format32bppArgb, true)] + [InlineData(PixelFormat.Format32bppPArgb, true)] + [InlineData(PixelFormat.Format64bppArgb, true)] + [InlineData(PixelFormat.Format64bppPArgb, true)] + [InlineData(PixelFormat.Format16bppGrayScale, false)] + [InlineData(PixelFormat.Format16bppRgb555, false)] + [InlineData(PixelFormat.Format16bppRgb565, false)] + [InlineData(PixelFormat.Format1bppIndexed, false)] + [InlineData(PixelFormat.Format24bppRgb, false)] + [InlineData(PixelFormat.Format32bppRgb, false)] + [InlineData(PixelFormat.Format48bppRgb, false)] + [InlineData(PixelFormat.Format4bppIndexed, false)] + [InlineData(PixelFormat.Format8bppIndexed, false)] + public void IsAlphaPixelFormat_ReturnsExpected(PixelFormat format, bool expected) + { + Assert.Equal(expected, Image.IsAlphaPixelFormat(format)); + } + + public static IEnumerable GetEncoderParameterList_ReturnsExpected_TestData() + { + yield return new object[] + { + ImageFormat.Tiff, + new Guid[] + { + Encoder.Compression.Guid, + Encoder.ColorDepth.Guid, + Encoder.SaveFlag.Guid, + new Guid(unchecked((int)0xa219bbc9), unchecked((short)0x0a9d), unchecked((short)0x4005), new byte[] { 0xa3, 0xee, 0x3a, 0x42, 0x1b, 0x8b, 0xb0, 0x6c }) /* Encoder.SaveAsCmyk.Guid */ + } + }; + +#if !NETFRAMEWORK + // NetFX doesn't support pointer-type encoder parameters, and doesn't define Encoder.ImageItems. Skip this test + // on NetFX. + yield return new object[] + { + ImageFormat.Jpeg, + new Guid[] + { + Encoder.Transformation.Guid, + Encoder.Quality.Guid, + Encoder.LuminanceTable.Guid, + Encoder.ChrominanceTable.Guid, + Encoder.ImageItems.Guid + } + }; +#endif + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(GetEncoderParameterList_ReturnsExpected_TestData))] + public void GetEncoderParameterList_ReturnsExpected(ImageFormat format, Guid[] expectedParameters) + { + if (PlatformDetection.IsNetFramework) + { + throw new SkipTestException("This is a known bug for .NET Framework"); + } + + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageEncoders(); + ImageCodecInfo codec = codecs.Single(c => c.FormatID == format.Guid); + + using (var bitmap = new Bitmap(1, 1)) + { + EncoderParameters paramList = bitmap.GetEncoderParameterList(codec.Clsid); + + Assert.Equal( + expectedParameters, + paramList.Param.Select(p => p.Encoder.Guid)); + } + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Framework throws ExternalException")] + [Fact] + public void Save_InvalidDirectory_ThrowsDirectoryNotFoundException() + { + using (var bitmap = new Bitmap(1, 1)) + { + var badTarget = System.IO.Path.Combine("NoSuchDirectory", "NoSuchFile"); + AssertExtensions.Throws(() => bitmap.Save(badTarget), $"The directory NoSuchDirectory of the filename {badTarget} does not exist."); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/BitmapDataTests.cs b/src/System.Drawing.Common/tests/Imaging/BitmapDataTests.cs new file mode 100644 index 00000000000..01c7bbc49e8 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/BitmapDataTests.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class BitmapDataTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + BitmapData bd = new BitmapData(); + Assert.Equal(0, bd.Height); + Assert.Equal(0, bd.Width); + Assert.Equal(0, bd.Reserved); + Assert.Equal(IntPtr.Zero, bd.Scan0); + Assert.Equal(0, bd.Stride); + Assert.Equal((PixelFormat)0, bd.PixelFormat); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void Height_SetValid_ReturnsExpected(int value) + { + BitmapData bd = new BitmapData(); + bd.Height = value; + Assert.Equal(value, bd.Height); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void Width_SetValid_ReturnsExpected(int value) + { + BitmapData bd = new BitmapData(); + bd.Width = value; + Assert.Equal(value, bd.Width); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void Reserved_SetValid_ReturnsExpected(int value) + { + BitmapData bd = new BitmapData(); + bd.Reserved = value; + Assert.Equal(value, bd.Reserved); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void Scan0_SetValid_ReturnsExpected(int value) + { + BitmapData bd = new BitmapData(); + bd.Scan0 = new IntPtr(value); + Assert.Equal(new IntPtr(value), bd.Scan0); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void Stride_SetValid_ReturnsExpected(int value) + { + BitmapData bd = new BitmapData(); + bd.Stride = value; + Assert.Equal(value, bd.Stride); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PixelFormat.DontCare)] + [InlineData(PixelFormat.Max)] + [InlineData(PixelFormat.Indexed)] + [InlineData(PixelFormat.Gdi)] + [InlineData(PixelFormat.Format16bppRgb555)] + [InlineData(PixelFormat.Format16bppRgb565)] + [InlineData(PixelFormat.Format24bppRgb)] + [InlineData(PixelFormat.Format32bppRgb)] + [InlineData(PixelFormat.Format1bppIndexed)] + [InlineData(PixelFormat.Format4bppIndexed)] + [InlineData(PixelFormat.Format8bppIndexed)] + [InlineData(PixelFormat.Alpha)] + [InlineData(PixelFormat.Format16bppArgb1555)] + [InlineData(PixelFormat.PAlpha)] + [InlineData(PixelFormat.Format32bppPArgb)] + [InlineData(PixelFormat.Extended)] + [InlineData(PixelFormat.Format16bppGrayScale)] + [InlineData(PixelFormat.Format48bppRgb)] + [InlineData(PixelFormat.Format64bppPArgb)] + [InlineData(PixelFormat.Canonical)] + [InlineData(PixelFormat.Format32bppArgb)] + [InlineData(PixelFormat.Format64bppArgb)] + public void PixelFormat_SetValid_ReturnsExpected(PixelFormat pixelFormat) + { + BitmapData bd = new BitmapData(); + bd.PixelFormat = pixelFormat; + Assert.Equal(pixelFormat, bd.PixelFormat); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PixelFormat_SetInvalid_ThrowsInvalidEnumException() + { + BitmapData bd = new BitmapData(); + Assert.ThrowsAny(() => bd.PixelFormat = (PixelFormat)(-1)); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/ColorMapTests.cs b/src/System.Drawing.Common/tests/Imaging/ColorMapTests.cs new file mode 100644 index 00000000000..ff3b1e5bac3 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/ColorMapTests.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class ColorMapTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + ColorMap cm = new ColorMap(); + Assert.Equal(new Color(), cm.OldColor); + Assert.Equal(new Color(), cm.NewColor); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NewColor_SetValid_ReturnsExpected() + { + ColorMap cm = new ColorMap(); + cm.NewColor = Color.AliceBlue; + Assert.Equal(Color.AliceBlue, cm.NewColor); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void OldColor_SetValid_ReturnsExpected() + { + ColorMap cm = new ColorMap(); + cm.OldColor = Color.AliceBlue; + Assert.Equal(Color.AliceBlue, cm.OldColor); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/ColorMatrixTests.cs b/src/System.Drawing.Common/tests/Imaging/ColorMatrixTests.cs new file mode 100644 index 00000000000..35d706af8fd --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/ColorMatrixTests.cs @@ -0,0 +1,285 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class ColorMatrixTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + ColorMatrix cm = new ColorMatrix(); + + Assert.Equal(1, cm.Matrix00); + Assert.Equal(1, cm.Matrix11); + Assert.Equal(1, cm.Matrix22); + Assert.Equal(1, cm.Matrix33); + Assert.Equal(1, cm.Matrix44); + Assert.Equal(0, cm.Matrix01); + Assert.Equal(0, cm.Matrix02); + Assert.Equal(0, cm.Matrix03); + Assert.Equal(0, cm.Matrix04); + Assert.Equal(0, cm.Matrix10); + Assert.Equal(0, cm.Matrix12); + Assert.Equal(0, cm.Matrix13); + Assert.Equal(0, cm.Matrix14); + Assert.Equal(0, cm.Matrix20); + Assert.Equal(0, cm.Matrix21); + Assert.Equal(0, cm.Matrix23); + Assert.Equal(0, cm.Matrix24); + Assert.Equal(0, cm.Matrix30); + Assert.Equal(0, cm.Matrix31); + Assert.Equal(0, cm.Matrix32); + Assert.Equal(0, cm.Matrix34); + Assert.Equal(0, cm.Matrix40); + Assert.Equal(0, cm.Matrix41); + Assert.Equal(0, cm.Matrix42); + Assert.Equal(0, cm.Matrix43); + } + + public static IEnumerable BadCtorParams + { + get + { + yield return new object[] { null, typeof(NullReferenceException) }; + yield return new object[] { new float[][] { }, typeof(IndexOutOfRangeException) }; + yield return new object[] { new float[][] { new float[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f } }, typeof(IndexOutOfRangeException) }; + yield return new object[] { new float[][] { + new float[] { 0.0f }, + new float[] { 1.0f }, + new float[] { 2.0f }, + new float[] { 3.0f }, + new float[] { 4.0f }, + new float[] { 5.0f } } + , typeof(IndexOutOfRangeException) }; + } + } + + public static float[][] IndexedColorMatrix + { + get + { + return new float[][] { + new float[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f}, + new float[] { 1.0f, 1.1f, 1.2f, 1.3f, 1.4f}, + new float[] { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f}, + new float[] { 3.0f, 3.1f, 3.2f, 3.3f, 3.4f}, + new float[] { 4.0f, 4.1f, 4.2f, 4.3f, 4.4f}, + }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(BadCtorParams))] + public void Ctor_BadValues_ThrowsExpectedException(float[][] newColorMatrix, Type expectedException) + { + Assert.Throws(expectedException, () => new ColorMatrix(newColorMatrix)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_TooBigArraySize_MapOnly4and4Elements() + { + ColorMatrix cm = new ColorMatrix(new float[][] { + new float[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f }, + new float[] { 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f }, + new float[] { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f }, + new float[] { 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.5f }, + new float[] { 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f }, + new float[] { 5.0f, 5.1f, 5.2f, 5.3f, 5.4f, 5.5f } + }); + + Assert.Equal(0.0f, cm.Matrix00); + Assert.Equal(0.1f, cm.Matrix01); + Assert.Equal(0.2f, cm.Matrix02); + Assert.Equal(0.3f, cm.Matrix03); + Assert.Equal(0.4f, cm.Matrix04); + Assert.Equal(1.0f, cm.Matrix10); + Assert.Equal(1.1f, cm.Matrix11); + Assert.Equal(1.2f, cm.Matrix12); + Assert.Equal(1.3f, cm.Matrix13); + Assert.Equal(1.4f, cm.Matrix14); + Assert.Equal(2.0f, cm.Matrix20); + Assert.Equal(2.1f, cm.Matrix21); + Assert.Equal(2.2f, cm.Matrix22); + Assert.Equal(2.3f, cm.Matrix23); + Assert.Equal(2.4f, cm.Matrix24); + Assert.Equal(3.0f, cm.Matrix30); + Assert.Equal(3.1f, cm.Matrix31); + Assert.Equal(3.2f, cm.Matrix32); + Assert.Equal(3.3f, cm.Matrix33); + Assert.Equal(3.4f, cm.Matrix34); + Assert.Equal(4.0f, cm.Matrix40); + Assert.Equal(4.1f, cm.Matrix41); + Assert.Equal(4.2f, cm.Matrix42); + Assert.Equal(4.3f, cm.Matrix43); + Assert.Equal(4.4f, cm.Matrix44); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AccessToNotExistingElement_ThrowsIndexOutOfRangeException() + { + ColorMatrix cm = new ColorMatrix(new float[][] { + new float[] { 0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f }, + new float[] { 1.0f, 1.1f, 1.2f, 1.3f, 1.4f, 1.5f }, + new float[] { 2.0f, 2.1f, 2.2f, 2.3f, 2.4f, 2.5f }, + new float[] { 3.0f, 3.1f, 3.2f, 3.3f, 3.4f, 3.5f }, + new float[] { 4.0f, 4.1f, 4.2f, 4.3f, 4.4f, 4.5f }, + new float[] { 5.0f, 5.1f, 5.2f, 5.3f, 5.4f, 5.5f } + }); + Assert.Throws(() => _ = cm[5, 5]); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_SetValue_ReturnsExpected() + { + ColorMatrix cm = new ColorMatrix(IndexedColorMatrix); + + Assert.Equal(0.0f, cm.Matrix00); + Assert.Equal(1.0f, cm.Matrix10); + Assert.Equal(2.0f, cm.Matrix20); + Assert.Equal(3.0f, cm.Matrix30); + Assert.Equal(4.0f, cm.Matrix40); + + Assert.Equal(0.1f, cm.Matrix01); + Assert.Equal(1.1f, cm.Matrix11); + Assert.Equal(2.1f, cm.Matrix21); + Assert.Equal(3.1f, cm.Matrix31); + Assert.Equal(4.1f, cm.Matrix41); + + Assert.Equal(0.2f, cm.Matrix02); + Assert.Equal(1.2f, cm.Matrix12); + Assert.Equal(2.2f, cm.Matrix22); + Assert.Equal(3.2f, cm.Matrix32); + Assert.Equal(4.2f, cm.Matrix42); + + Assert.Equal(0.3f, cm.Matrix03); + Assert.Equal(1.3f, cm.Matrix13); + Assert.Equal(2.3f, cm.Matrix23); + Assert.Equal(3.3f, cm.Matrix33); + Assert.Equal(4.3f, cm.Matrix43); + + Assert.Equal(0.4f, cm.Matrix04); + Assert.Equal(1.4f, cm.Matrix14); + Assert.Equal(2.4f, cm.Matrix24); + Assert.Equal(3.4f, cm.Matrix34); + Assert.Equal(4.4f, cm.Matrix44); + + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 5; j++) + { + Assert.Equal(IndexedColorMatrix[i][j], cm[i, j]); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MatrixElement_SetValues_ReturnsExpected() + { + ColorMatrix cm = new ColorMatrix(); + + cm.Matrix00 = 1; + cm.Matrix01 = 2; + cm.Matrix02 = 3; + cm.Matrix03 = 4; + cm.Matrix04 = 5; + cm.Matrix10 = 6; + cm.Matrix11 = 7; + cm.Matrix12 = 8; + cm.Matrix13 = 9; + cm.Matrix14 = 10; + cm.Matrix20 = 11; + cm.Matrix21 = 12; + cm.Matrix22 = 13; + cm.Matrix23 = 14; + cm.Matrix24 = 15; + cm.Matrix30 = 16; + cm.Matrix31 = 17; + cm.Matrix32 = 18; + cm.Matrix33 = 19; + cm.Matrix34 = 20; + cm.Matrix40 = 21; + cm.Matrix41 = 22; + cm.Matrix42 = 23; + cm.Matrix43 = 24; + cm.Matrix44 = 25; + + Assert.Equal(1, cm.Matrix00); + Assert.Equal(2, cm.Matrix01); + Assert.Equal(3, cm.Matrix02); + Assert.Equal(4, cm.Matrix03); + Assert.Equal(5, cm.Matrix04); + Assert.Equal(6, cm.Matrix10); + Assert.Equal(7, cm.Matrix11); + Assert.Equal(8, cm.Matrix12); + Assert.Equal(9, cm.Matrix13); + Assert.Equal(10, cm.Matrix14); + Assert.Equal(11, cm.Matrix20); + Assert.Equal(12, cm.Matrix21); + Assert.Equal(13, cm.Matrix22); + Assert.Equal(14, cm.Matrix23); + Assert.Equal(15, cm.Matrix24); + Assert.Equal(16, cm.Matrix30); + Assert.Equal(17, cm.Matrix31); + Assert.Equal(18, cm.Matrix32); + Assert.Equal(19, cm.Matrix33); + Assert.Equal(20, cm.Matrix34); + Assert.Equal(21, cm.Matrix40); + Assert.Equal(22, cm.Matrix41); + Assert.Equal(23, cm.Matrix42); + Assert.Equal(24, cm.Matrix43); + Assert.Equal(25, cm.Matrix44); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MatrixElementByIndexer_SetValue_ReturnsExpetecd() + { + ColorMatrix cm = new ColorMatrix(IndexedColorMatrix); + + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 5; j++) + { + cm[i, j] = IndexedColorMatrix[i][j]; + } + } + + for (int i = 0; i < 5; i++) + { + for (int j = 0; j < 5; j++) + { + Assert.Equal(IndexedColorMatrix[i][j], cm[i, j]); + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs b/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs new file mode 100644 index 00000000000..122e00d0307 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/EncoderParameterTests.cs @@ -0,0 +1,367 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class EncoderParameterTests + { + private static readonly Encoder s_anyEncoder = Encoder.ChrominanceTable; + + private void CheckEncoderParameter(EncoderParameter encoderParameter, Encoder expectedEncoder, EncoderParameterValueType expectedType, int expectedNumberOfValues) + { + Assert.Equal(expectedEncoder.Guid, encoderParameter.Encoder.Guid); + Assert.Equal(expectedType, encoderParameter.ValueType); + Assert.Equal(expectedType, encoderParameter.Type); + Assert.Equal(expectedNumberOfValues, encoderParameter.NumberOfValues); + } + + public static IEnumerable Ctor_Encoder_Byte_TestData + { + get + { + yield return new object[] { Encoder.ChrominanceTable, byte.MinValue }; + yield return new object[] { Encoder.ColorDepth, byte.MinValue }; + yield return new object[] { Encoder.Compression, byte.MinValue }; + yield return new object[] { Encoder.LuminanceTable, byte.MinValue }; + yield return new object[] { Encoder.Quality, byte.MinValue }; + yield return new object[] { Encoder.RenderMethod, byte.MinValue }; + yield return new object[] { Encoder.SaveFlag, byte.MinValue }; + yield return new object[] { Encoder.ScanMethod, byte.MinValue }; + yield return new object[] { Encoder.Transformation, byte.MinValue }; + yield return new object[] { Encoder.Version, byte.MinValue }; + yield return new object[] { new Encoder(Guid.NewGuid()), byte.MinValue }; + yield return new object[] { new Encoder(Guid.NewGuid()), 1 }; + yield return new object[] { new Encoder(Guid.NewGuid()), byte.MaxValue }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Encoder_Byte_TestData))] + public void Ctor_Encoder_Byte(Encoder encoder, byte value) + { + using (EncoderParameter ep = new EncoderParameter(encoder, value)) + { + CheckEncoderParameter(ep, encoder, EncoderParameterValueType.ValueTypeByte, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(false, EncoderParameterValueType.ValueTypeByte)] + [InlineData(true, EncoderParameterValueType.ValueTypeUndefined)] + public void Ctor_Encoder_ByteValue_Bool(bool undefined, EncoderParameterValueType expected) + { + EncoderParameter ep = new EncoderParameter(s_anyEncoder, 0, undefined); + CheckEncoderParameter(ep, s_anyEncoder, expected, 1); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(short.MinValue)] + [InlineData(short.MaxValue)] + public void Ctor_Encoder_Short(short value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeShort, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(long.MinValue)] + [InlineData(long.MaxValue)] + public void Ctor_Encoder_Long(long value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeLong, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue, int.MaxValue)] + [InlineData(10, 5)] + [InlineData(-10, -5)] + public void Ctor_Encoder_Numerator_Denominator(int numerator, int denominator) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, numerator, denominator)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeRational, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0, 0, 0)] + [InlineData(1, 2, 3, 4)] + public void Ctor_Encoder_Numerator1_Denominator1_Numerator2_Denominator2(int numerator1, int denominator1, int numerator2, int denominator2) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, numerator1, denominator1, numerator2, denominator2)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeRationalRange, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(1, 2)] + public void Ctor_Encoder_RangeBegin_RangeEnd(long rangeBegin, long rangeEnd) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, rangeBegin, rangeEnd)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeLongRange, 1); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("someStringValue")] + [InlineData("")] + public void Ctor_Encoder_String(string value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeAscii, value.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new byte[] { })] + [InlineData(new byte[] { 0, 1, 2, 3 })] + public void Ctor_Encoder_ByteArray(byte[] value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeByte, value.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new byte[] { 1, 2 }, false, EncoderParameterValueType.ValueTypeByte)] + [InlineData(new byte[] { 1, 2 }, true, EncoderParameterValueType.ValueTypeUndefined)] + public void Ctor_Encoder_ByteArray_Bool(byte[] value, bool undefined, EncoderParameterValueType expected) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value, undefined)) + { + CheckEncoderParameter(ep, s_anyEncoder, expected, value.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new short[] { })] + [InlineData(new short[] { 0, 1, 2, 3 })] + public void Ctor_Encoder_ShortArray(short[] value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeShort, value.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new long[] { })] + [InlineData(new long[] { 0, 1, 2, 3 })] + public void Ctor_Encoder_LongArray(long[] value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeLong, value.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new int[] { 0, 1, 2, 3 }, new int[] { 5, 6, 7, 8 })] + public void Ctor_Encoder_NumeratorArray_DenominatorArray(int[] numerator, int[] denominator) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, numerator, denominator)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeRational, numerator.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new long[] { 0, 1, 2, 3 }, new long[] { 5, 6, 7, 8 })] + public void Ctor_Encoder_RangeBeginArray_RangeEndArray(long[] rangeBegin, long[] rangeEnd) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, rangeBegin, rangeEnd)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeLongRange, rangeBegin.Length); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new int[] { 0, 1, 2, 3 }, new int[] { 4, 5, 6, 7 }, new int[] { 8, 9, 10, 11 }, new int[] { 12, 13, 14, 15 })] + public void Ctor_Encoder_Numerator1Array_Denominator1Array_Numerator2Array_Denominator2Array(int[] numerator1, int[] denominator1, int[] numerator2, int[] denominator2) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, numerator1, denominator1, numerator2, denominator2)) + { + CheckEncoderParameter(ep, s_anyEncoder, EncoderParameterValueType.ValueTypeRationalRange, numerator1.Length); + } + } + + public static IEnumerable Encoder_NumberOfValues_TestData + { + get + { + yield return new object[] { 0, EncoderParameterValueType.ValueTypeAscii, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeByte, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeLong, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeLongRange, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeRational, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeRationalRange, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeShort, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeUndefined, IntPtr.Zero }; + yield return new object[] { 0, EncoderParameterValueType.ValueTypeUndefined, IntPtr.Zero }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Encoder_NumberOfValues_TestData))] + public void Ctor_Encoder_NumberOfValues_Type_Value(int numberOfValues, EncoderParameterValueType type, IntPtr value) + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, numberOfValues, type, value)) + { + CheckEncoderParameter(ep, s_anyEncoder, type, numberOfValues); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Encoder_ReturnsExpecetd() + { + Encoder encoder = new Encoder(Guid.NewGuid()); + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, 0) + { + Encoder = encoder + }) + { + Assert.Equal(encoder.Guid, ep.Encoder.Guid); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Encoder_NumberOfValues_NotExistingType_ThrowsInvalidOperationException() + { + Assert.Throws(() => new EncoderParameter(s_anyEncoder, 1, (EncoderParameterValueType)999, IntPtr.Zero)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new int[] { 1, 2 }, new int[] { 1 }, new int[] { 1 }, new int[] { 1 }, typeof(ArgumentException))] + [InlineData(new int[] { 1 }, new int[] { 1, 2 }, new int[] { 1 }, new int[] { 1 }, typeof(ArgumentException))] + [InlineData(null, new int[] { 1 }, new int[] { 1 }, new int[] { 1 }, typeof(NullReferenceException))] + [InlineData(new int[] { 1 }, null, new int[] { 1 }, new int[] { 1 }, typeof(NullReferenceException))] + [InlineData(new int[] { 1 }, new int[] { 1 }, null, new int[] { 1 }, typeof(NullReferenceException))] + [InlineData(new int[] { 1 }, new int[] { 1 }, new int[] { 1 }, null, typeof(NullReferenceException))] + public void Ctor_Encoder_Numerator1Array_Denominator1Array_Numerator2Array_Denominator2Array_InvalidParameters_ThrowsExpected(int[] numerator1, int[] denominator1, int[] numerator2, int[] denominator2, Type expected) + { + Assert.Throws(expected, () => new EncoderParameter(s_anyEncoder, numerator1, denominator1, numerator2, denominator2)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Encoder_Null_ThrowsNullReferenceException() + { + using (EncoderParameter ep = new EncoderParameter(s_anyEncoder, 0)) + { + Assert.Throws(() => ep.Encoder = null); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new int[] { 0 }, new int[] { 0, 1 }, typeof(ArgumentException))] + [InlineData(new int[] { 0, 1 }, new int[] { 0 }, typeof(ArgumentException))] + [InlineData(new int[] { 0, 1 }, null, typeof(NullReferenceException))] + [InlineData(null, new int[] { 0, 1 }, typeof(NullReferenceException))] + public void Ctor_Numerator_Denominator_IvalidValues_ThrowsExpected(int[] numerator, int[] denominator, Type expected) + { + Assert.Throws(expected, () => new EncoderParameter(s_anyEncoder, numerator, denominator)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new long[] { 0 }, new long[] { 0, 1 }, typeof(ArgumentException))] + [InlineData(new long[] { 0, 1 }, new long[] { 0 }, typeof(ArgumentException))] + [InlineData(new long[] { 0, 1 }, null, typeof(NullReferenceException))] + [InlineData(null, new long[] { 0, 1 }, typeof(NullReferenceException))] + public void Ctor_RangeBegin_RangeEnd_InvalidValues_ThrowsExpected(long[] rangeBegin, long[] rangeEnd, Type expected) + { + Assert.Throws(expected, () => new EncoderParameter(s_anyEncoder, rangeBegin, rangeEnd)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Encoder_NullString_ThrowsNullReferenceException() + { + Assert.Throws(() => new EncoderParameter(s_anyEncoder, (string)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Encoder_ByteArray_ThrowsNullReferenceException() + { + Assert.Throws(() => new EncoderParameter(s_anyEncoder, (byte[])null)); + } + + public static IEnumerable EncoderParameterCtor_NullEncoder_TestData + { + get + { + yield return new object[] { new Action(() => new EncoderParameter(null, (byte)0)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, (byte)0, false)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, (short)0)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, numerator: 0, denominator: 0)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, rangebegin: 0, rangeend: 0)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, 0, 0, 0, 0)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, "anyString")) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new byte[] { })) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new byte[] { }, false)) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new short[] { })) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new long[] { })) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new int[] { }, new int[] { })) }; + yield return new object[] { new Action(() => new EncoderParameter(null, new long[] { }, new long[] { })) }; + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullEncoder_ThrowsNullReferenceException() + { + Assert.Throws(() => new EncoderParameter(null, (byte)0)); + Assert.Throws(() => new EncoderParameter(null, (byte)0, false)); + Assert.Throws(() => new EncoderParameter(null, (short)0)); + Assert.Throws(() => new EncoderParameter(null, numerator: 0, denominator: 0)); + Assert.Throws(() => new EncoderParameter(null, rangebegin: 0, rangeend: 0)); + Assert.Throws(() => new EncoderParameter(null, 0, 0, 0, 0)); + Assert.Throws(() => new EncoderParameter(null, "anyString")); + Assert.Throws(() => new EncoderParameter(null, new byte[] { })); + Assert.Throws(() => new EncoderParameter(null, new byte[] { }, false)); + Assert.Throws(() => new EncoderParameter(null, new short[] { })); + Assert.Throws(() => new EncoderParameter(null, new long[] { })); + Assert.Throws(() => new EncoderParameter(null, new int[] { }, new int[] { })); + Assert.Throws(() => new EncoderParameter(null, new long[] { }, new long[] { })); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(EncoderParameterValueType.ValueTypeShort, (int.MaxValue / 2) + 1, typeof(OverflowException))] + [InlineData(EncoderParameterValueType.ValueTypeLong, (int.MaxValue / 4) + 1, typeof(OverflowException))] + [InlineData(EncoderParameterValueType.ValueTypeRational, (int.MaxValue / 8) + 1, typeof(OverflowException))] + [InlineData(EncoderParameterValueType.ValueTypeLongRange, (int.MaxValue / 8) + 1, typeof(OverflowException))] + [InlineData(EncoderParameterValueType.ValueTypeRationalRange, (int.MaxValue / 16) + 1, typeof(OverflowException))] + public void Ctor_Encoder_TooBigNumberOfValues_Type_Value_AccessViolationException(EncoderParameterValueType type, int numberOfValues, Type expected) + { + Assert.Throws(expected, () => new EncoderParameter(s_anyEncoder, numberOfValues, type, IntPtr.Zero)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(int.MinValue)] + // This test may depend on amount of RAM and system configuration and load. + public void Ctor_Encoder_NegativeNumberOfValues_Type_Value_OutOfMemoryException(int numberOfValues) + { + if (PlatformDetection.Is32BitProcess) + { + throw new SkipTestException("backwards compatibility on 32 bit platforms may not throw"); + } + + IntPtr anyValue = IntPtr.Zero; + EncoderParameterValueType anyTypw = EncoderParameterValueType.ValueTypeAscii; + Assert.Throws(() => new EncoderParameter(s_anyEncoder, numberOfValues, anyTypw, anyValue)); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/EncoderParametersTests.cs b/src/System.Drawing.Common/tests/Imaging/EncoderParametersTests.cs new file mode 100644 index 00000000000..6a8b1c9b29b --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/EncoderParametersTests.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class EncoderParametersTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + using (EncoderParameters ep = new EncoderParameters()) + { + Assert.NotNull(ep.Param); + Assert.Equal(new EncoderParameter[1], ep.Param); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + public void Ctor_Count_Default(int count) + { + using (EncoderParameters ep = new EncoderParameters(count)) + { + Assert.NotNull(ep.Param); + Assert.Equal(new EncoderParameter[count], ep.Param); + } + } + + public static IEnumerable Param_TestData + { + get + { + yield return new object[] { new EncoderParameter[1] }; + yield return new object[] { new EncoderParameter[1] { new EncoderParameter(Encoder.ChrominanceTable, 0) } }; + yield return new object[] { new EncoderParameter[1] { null } }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Param_TestData))] + public void Param_Success(EncoderParameter[] param) + { + using (EncoderParameters ep = new EncoderParameters()) + { + ep.Param = param; + Assert.Equal(param, ep.Param); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Param_TestData))] + public void Dispose_Success(EncoderParameter[] param) + { + EncoderParameters ep = new EncoderParameters(); + ep.Param = param; + ep.Dispose(); + Assert.Null(ep.Param); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/EncoderTests.cs b/src/System.Drawing.Common/tests/Imaging/EncoderTests.cs new file mode 100644 index 00000000000..e058464cc20 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/EncoderTests.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class EncoderTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Guid() + { + Guid guid = Guid.NewGuid(); + Encoder encoder = new Encoder(guid); + Assert.Equal(guid, encoder.Guid); + } + + public static IEnumerable EncoderTestData + { + get + { + yield return new object[] { Encoder.Compression.Guid, new Guid(unchecked((int)0xe09d739d), unchecked((short)0xccd4), unchecked((short)0x44ee), new byte[] { 0x8e, 0xba, 0x3f, 0xbf, 0x8b, 0xe4, 0xfc, 0x58 }) }; + yield return new object[] { Encoder.ColorDepth.Guid, new Guid(0x66087055, unchecked((short)0xad66), unchecked((short)0x4c7c), new byte[] { 0x9a, 0x18, 0x38, 0xa2, 0x31, 0x0b, 0x83, 0x37 }) }; + yield return new object[] { Encoder.ScanMethod.Guid, new Guid(0x3a4e2661, (short)0x3109, (short)0x4e56, new byte[] { 0x85, 0x36, 0x42, 0xc1, 0x56, 0xe7, 0xdc, 0xfa }) }; + yield return new object[] { Encoder.Version.Guid, new Guid(0x24d18c76, unchecked((short)0x814a), unchecked((short)0x41a4), new byte[] { 0xbf, 0x53, 0x1c, 0x21, 0x9c, 0xcc, 0xf7, 0x97 }) }; + yield return new object[] { Encoder.RenderMethod.Guid, new Guid(0x6d42c53a, (short)0x229a, (short)0x4825, new byte[] { 0x8b, 0xb7, 0x5c, 0x99, 0xe2, 0xb9, 0xa8, 0xb8 }) }; + yield return new object[] { Encoder.Quality.Guid, new Guid(0x1d5be4b5, unchecked((short)0xfa4a), unchecked((short)0x452d), new byte[] { 0x9c, 0xdd, 0x5d, 0xb3, 0x51, 0x05, 0xe7, 0xeb }) }; + yield return new object[] { Encoder.Transformation.Guid, new Guid(unchecked((int)0x8d0eb2d1), unchecked((short)0xa58e), unchecked((short)0x4ea8), new byte[] { 0xaa, 0x14, 0x10, 0x80, 0x74, 0xb7, 0xb6, 0xf9 }) }; + yield return new object[] { Encoder.LuminanceTable.Guid, new Guid(unchecked((int)0xedb33bce), unchecked((short)0x0266), unchecked((short)0x4a77), new byte[] { 0xb9, 0x04, 0x27, 0x21, 0x60, 0x99, 0xe7, 0x17 }) }; + yield return new object[] { Encoder.ChrominanceTable.Guid, new Guid(unchecked((int)0xf2e455dc), unchecked((short)0x09b3), unchecked((short)0x4316), new byte[] { 0x82, 0x60, 0x67, 0x6a, 0xda, 0x32, 0x48, 0x1c }) }; + yield return new object[] { Encoder.SaveFlag.Guid, new Guid(unchecked((int)0x292266fc), unchecked((short)0xac40), unchecked((short)0x47bf), new byte[] { 0x8c, 0xfc, 0xa8, 0x5b, 0x89, 0xa6, 0x55, 0xde }) }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EncoderTestData))] + public void DefinedEncoders_ReturnsExpected(Guid defined, Guid expected) + { + Assert.Equal(expected, defined); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/FrameDimensionTests.cs b/src/System.Drawing.Common/tests/Imaging/FrameDimensionTests.cs new file mode 100644 index 00000000000..8e5d24fc695 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/FrameDimensionTests.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class FrameDimensionTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Guid() + { + Guid guid = Guid.NewGuid(); + FrameDimension fd = new FrameDimension(guid); + Assert.Equal(guid, fd.Guid); + } + + public static IEnumerable ImageFormatGuidTestData + { + get + { + yield return new object[] { new Guid("{6aedbd6d-3fb5-418a-83a6-7f45229dc872}"), FrameDimension.Time }; + yield return new object[] { new Guid("{84236f7b-3bd3-428f-8dab-4ea1439ca315}"), FrameDimension.Resolution }; + yield return new object[] { new Guid("{7462dc86-6180-4c7e-8e3f-ee7333a7a483}"), FrameDimension.Page }; + yield return new object[] { new Guid("48749428-316f-496a-ab30-c819a92b3137"), new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")) }; + } + } + + public static IEnumerable FrameDimensionEqualsTestData + { + get + { + yield return new object[] { new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")), true }; + yield return new object[] { new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new FrameDimension(new Guid("b96b3cad-0728-11d3-9d7b-0000f81ef32e")), false }; + yield return new object[] { new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")), null, false }; + yield return new object[] { new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new object(), false }; + } + } + + public static IEnumerable FrameDimensionToStringTestData + { + get + { + yield return new object[] { "Time", FrameDimension.Time }; + yield return new object[] { "Resolution", FrameDimension.Resolution }; + yield return new object[] { "Page", FrameDimension.Page }; + yield return new object[] { "[FrameDimension: 48749428-316f-496a-ab30-c819a92b3137]", new FrameDimension(new Guid("48749428-316f-496a-ab30-c819a92b3137")) }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ImageFormatGuidTestData))] + public void Guid_ReturnsExpected(Guid expected, FrameDimension frameDimension) + { + Assert.Equal(expected, frameDimension.Guid); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FrameDimensionEqualsTestData))] + public void Equals_Object_ReturnsExpected(FrameDimension frameDimension, object obj, bool result) + { + Assert.Equal(result, frameDimension.Equals(obj)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHashCode_Success() + { + Guid guid = Guid.NewGuid(); + Assert.Equal(guid.GetHashCode(), new FrameDimension(guid).GetHashCode()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(FrameDimensionToStringTestData))] + public void ToString_ReturnsExpected(string expected, FrameDimension imageFormat) + { + Assert.Equal(expected, imageFormat.ToString()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/ImageAttributesTests.cs b/src/System.Drawing.Common/tests/Imaging/ImageAttributesTests.cs new file mode 100644 index 00000000000..5898c637bef --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/ImageAttributesTests.cs @@ -0,0 +1,1486 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Drawing.Tests; +using System.IO; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class ImageAttributesTests + { + private readonly Rectangle _rectangle = new Rectangle(0, 0, 64, 64); + private readonly Color _actualYellow = Color.FromArgb(255, 255, 255, 0); + private readonly Color _actualGreen = Color.FromArgb(255, 0, 255, 0); + private readonly Color _expectedRed = Color.FromArgb(255, 255, 0, 0); + private readonly Color _expectedBlack = Color.FromArgb(255, 0, 0, 0); + private readonly ColorMatrix _greenComponentToZeroColorMatrix = new ColorMatrix(new float[][] + { + new float[] {1, 0, 0, 0, 0}, + new float[] {0, 0, 0, 0, 0}, + new float[] {0, 0, 1, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {0, 0, 0, 0, 0}, + }); + + private readonly ColorMatrix _grayMatrix = new ColorMatrix(new float[][] { + new float[] {1, 0, 0, 0, 0}, + new float[] {0, 2, 0, 0, 0}, + new float[] {0, 0, 3, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {0, 0, 0, 0, 0}, + }); + + private readonly ColorMap[] _yellowToRedColorMap = new ColorMap[] + { + new ColorMap() { OldColor = Color.FromArgb(255, 255, 255, 0), NewColor = Color.FromArgb(255, 255, 0, 0) } + }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default_Success() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix); + + using (ImageAttributes clone = Assert.IsAssignableFrom(imageAttr.Clone())) + { + bitmap.SetPixel(0, 0, _actualYellow); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, clone); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.Clone()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrix_ColorMatrix_Success() + { + using (var brush = new SolidBrush(_actualGreen)) + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix); + bitmap.SetPixel(0, 0, _actualYellow); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + + graphics.FillRectangle(brush, _rectangle); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedBlack, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable ColorMatrix_DropShadowRepaintWhenAreaIsSmallerThanTheFilteredElement_TestData() + { + yield return new object[] { Color.FromArgb(100, 255, 0, 0) }; + yield return new object[] { Color.FromArgb(255, 255, 155, 155) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorMatrix_DropShadowRepaintWhenAreaIsSmallerThanTheFilteredElement_TestData))] + public void SetColorMatrix_ColorMatrixI_Success(Color color) + { + ColorMatrix colorMatrix = new ColorMatrix(new float[][] + { + new float[] {1, 0, 0, 0, 0}, + new float[] {0, 1, 0, 0, 0}, + new float[] {0, 0, 1, 0, 0}, + new float[] {0, 0, 0, 0.5f, 0}, + new float[] {0, 0, 0, 0, 1}, + }); + + using (var brush = new SolidBrush(color)) + using (var bitmapBig = new Bitmap(200, 100)) + using (var bitmapSmall = new Bitmap(100, 100)) + using (var graphicsSmallBitmap = Graphics.FromImage(bitmapSmall)) + using (var graphicsBigBitmap = Graphics.FromImage(bitmapBig)) + using (var imageAttr = new ImageAttributes()) + { + graphicsSmallBitmap.FillRectangle(Brushes.White, 0, 0, 100, 100); + graphicsSmallBitmap.FillEllipse(brush, 0, 0, 100, 100); + graphicsBigBitmap.FillRectangle(Brushes.White, 0, 0, 200, 100); + imageAttr.SetColorMatrix(colorMatrix); + graphicsBigBitmap.DrawImage(bitmapSmall, new Rectangle(0, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel, null); + graphicsBigBitmap.DrawImage(bitmapSmall, new Rectangle(100, 0, 100, 100), 0, 0, 100, 100, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 255, 155, 155), bitmapBig.GetPixel(50, 50)); + Assert.Equal(Color.FromArgb(255, 255, 205, 205), bitmapBig.GetPixel(150, 50)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrix_ColorMatrixFlags_Success() + { + var grayShade = Color.FromArgb(255, 100, 100, 100); + + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + bitmap.SetPixel(0, 0, _actualYellow); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + + bitmap.SetPixel(0, 0, grayShade); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.SkipGrays); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(grayShade, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable ColorAdjustType_TestData() + { + yield return new object[] { ColorAdjustType.Default }; + yield return new object[] { ColorAdjustType.Bitmap }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void SetColorMatrix_ColorMatrixDefaultFlagType_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var brush = new SolidBrush(_actualYellow)) + using (var pen = new Pen(brush)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedBlack, bitmap.GetPixel(0, 0)); + + graphics.FillRectangle(brush, _rectangle); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + + graphics.DrawRectangle(pen, _rectangle); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable ColorAdjustTypeI_TestData() + { + yield return new object[] { ColorAdjustType.Brush }; + yield return new object[] { ColorAdjustType.Pen }; + yield return new object[] { ColorAdjustType.Text }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void SetColorMatrix_ColorMatrixDefaultFlagTypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var brush = new SolidBrush(_actualYellow)) + using (var pen = new Pen(brush)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrix_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrix_NullMatrix_ThrowsArgumentException() + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(null)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(null, ColorMatrixFlag.Default)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrix(null, ColorMatrixFlag.Default, ColorAdjustType.Default)); + } + } + + public static IEnumerable ColorAdjustType_InvalidTypes_TestData() + { + yield return new object[] { (ColorAdjustType.Default - 1) }; + yield return new object[] { ColorAdjustType.Count }; + yield return new object[] { ColorAdjustType.Any }; + yield return new object[] { (ColorAdjustType.Any + 1) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetColorMatrix_InvalidTypes_ThrowsInvalidEnumArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type)); + } + } + + public static IEnumerable ColorMatrixFlag_InvalidFlags_TestData() + { + yield return new object[] { (ColorMatrixFlag.Default - 1) }; + yield return new object[] { ColorMatrixFlag.AltGrays }; + yield return new object[] { (ColorMatrixFlag.AltGrays + 1) }; + yield return new object[] { (ColorMatrixFlag)int.MinValue }; + yield return new object[] { (ColorMatrixFlag)int.MaxValue }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/2337", TestRuntimes.Mono)] + public void SetColorMatrix_InvalidFlags_ThrowsArgumentException(ColorMatrixFlag flag) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, flag)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, flag, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearColorMatrix_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix); + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix); + imageAttr.ClearColorMatrix(); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable ColorAdjustType_AllTypesAllowed_TestData() + { + yield return new object[] { ColorAdjustType.Default }; + yield return new object[] { ColorAdjustType.Bitmap }; + yield return new object[] { ColorAdjustType.Brush }; + yield return new object[] { ColorAdjustType.Pen }; + yield return new object[] { ColorAdjustType.Text }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearColorMatrix_DefaultFlagType_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var brush = new SolidBrush(_actualYellow)) + using (var pen = new Pen(brush)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, ColorMatrixFlag.Default, type); + imageAttr.ClearColorMatrix(type); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + + graphics.FillRectangle(brush, _rectangle); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualYellow, bitmap.GetPixel(0, 0)); + + graphics.DrawRectangle(pen, _rectangle); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualYellow, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearColorMatrix_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearColorMatrix()); + AssertExtensions.Throws(null, () => imageAttr.ClearColorMatrix(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearColorMatrix_InvalidTypes_ThrowsInvalidEnumArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearColorMatrix(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrices_ColorMatrixGrayMatrix_Success() + { + using (var brush = new SolidBrush(_actualGreen)) + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix); + bitmap.SetPixel(0, 0, _actualYellow); + bitmap.SetPixel(1, 1, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + Assert.Equal(Color.FromArgb(255, 100, 0, 100), bitmap.GetPixel(1, 1)); + } + } + + public static IEnumerable SetColorMatrices_Flags_TestData() + { + yield return new object[] { ColorMatrixFlag.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 0, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 200, 255) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetColorMatrices_Flags_TestData))] + public void SetColorMatrices_ColorMatrixGrayMatrixFlags_Success(ColorMatrixFlag flag, Color grayShade, Color expectedGrayShade) + { + using (var brush = new SolidBrush(_actualGreen)) + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, flag); + bitmap.SetPixel(0, 0, _actualYellow); + bitmap.SetPixel(1, 1, grayShade); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + Assert.Equal(expectedGrayShade, bitmap.GetPixel(1, 1)); + } + } + + public static IEnumerable SetColorMatrices_FlagsTypes_TestData() + { + yield return new object[] { ColorMatrixFlag.Default, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 0, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 200, 255) }; + yield return new object[] { ColorMatrixFlag.Default, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 0, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 100, 200, 255) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetColorMatrices_FlagsTypes_TestData))] + public void SetColorMatrices_ColorMatrixGrayMatrixFlagsTypes_Success + (ColorMatrixFlag flag, ColorAdjustType type, Color grayShade, Color expectedGrayShade) + { + using (var brush = new SolidBrush(_actualGreen)) + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, flag, type); + bitmap.SetPixel(0, 0, _actualYellow); + bitmap.SetPixel(1, 1, grayShade); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedRed, bitmap.GetPixel(0, 0)); + Assert.Equal(expectedGrayShade, bitmap.GetPixel(1, 1)); + } + } + + public static IEnumerable SetColorMatrices_FlagsTypesI_TestData() + { + yield return new object[] { ColorMatrixFlag.Default, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.Default, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.Default, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.SkipGrays, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorMatrixFlag.AltGrays, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetColorMatrices_FlagsTypesI_TestData))] + public void SetColorMatrices_ColorMatrixGrayMatrixFlagsTypesI_Success(ColorMatrixFlag flag, ColorAdjustType type, Color grayShade) + { + using (var brush = new SolidBrush(_actualGreen)) + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, flag, type); + bitmap.SetPixel(0, 0, _actualYellow); + bitmap.SetPixel(1, 1, grayShade); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualYellow, bitmap.GetPixel(0, 0)); + Assert.Equal(grayShade, bitmap.GetPixel(1, 1)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrices_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, ColorMatrixFlag.Default)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorMatrices_NullMatrices_ThrowsArgumentException() + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(null, _grayMatrix)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(null, _grayMatrix, ColorMatrixFlag.Default)); + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, null, ColorMatrixFlag.AltGrays)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrices(null, _grayMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, null, ColorMatrixFlag.AltGrays, ColorAdjustType.Default)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetColorMatrices_InvalidTypes_ThrowsInvalidEnumArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, ColorMatrixFlag.Default, type)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(ColorMatrixFlag.Default - 1)] + [InlineData(ColorMatrixFlag.AltGrays + 1)] + [InlineData((ColorMatrixFlag)int.MinValue)] + [InlineData((ColorMatrixFlag)int.MaxValue)] + public void SetColorMatrices_InvalidFlags_ThrowsArgumentException(ColorMatrixFlag flag) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, flag)); + AssertExtensions.Throws(null, () => + imageAttr.SetColorMatrices(_greenComponentToZeroColorMatrix, _grayMatrix, flag, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetThreshold_Threshold_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetThreshold(0.7f); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 230, 50, 220)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 255, 0, 255), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void SetThreshold_ThresholdType_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetThreshold(0.7f, type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 230, 50, 220)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 255, 0, 255), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void SetThreshold_ThresholdTypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetThreshold(0.7f, type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 230, 50, 220)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 230, 50, 220), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetThreshold_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetThreshold(0.5f)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetThreshold_InvalidType_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetThreshold(0.5f, type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearThreshold_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetThreshold(0.7f); + imageAttr.ClearThreshold(); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 230, 50, 220)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 230, 50, 220), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearThreshold_ThresholdTypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetThreshold(0.7f, type); + imageAttr.ClearThreshold(type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 230, 50, 220)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 230, 50, 220), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearThreshold_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearThreshold(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearThreshold_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearThreshold(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetGamma_Gamma_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 33, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void SetGamma_GammaType_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 33, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void SetGamma_GammaTypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetGamma_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetGamma(2.2f)); + AssertExtensions.Throws(null, () => imageAttr.SetGamma(2.2f, ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetGamma_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetGamma(2.2f, type)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearGamma_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + imageAttr.ClearGamma(type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearGamma_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearGamma(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearGamma_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearGamma(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetNoOp_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix); + imageAttr.SetNoOp(); + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void SetNoOp_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + imageAttr.SetNoOp(type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetNoOp_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetNoOp()); + AssertExtensions.Throws(null, () => imageAttr.SetNoOp(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetNoOp_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetNoOp(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearNoOp_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix); + imageAttr.SetNoOp(); + imageAttr.ClearNoOp(); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_expectedBlack, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void ClearNoOp_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + imageAttr.SetNoOp(type); + imageAttr.ClearNoOp(type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 33, 0, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void ClearNoOp_TypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetGamma(2.2f, type); + imageAttr.SetColorMatrix(_greenComponentToZeroColorMatrix, ColorMatrixFlag.Default, type); + imageAttr.SetNoOp(type); + imageAttr.ClearNoOp(type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 255, 0)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 255, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearNoOp_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearNoOp()); + AssertExtensions.Throws(null, () => imageAttr.ClearNoOp(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearNoOp_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearNoOp(type)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22784")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorKey_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150)); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(0, 0, 0, 0), bitmap.GetPixel(0, 0)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22784")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void SetColorKey_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150), type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(0, 0, 0, 0), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void SetColorKey_TypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150), type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 100, 100), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetColorKey_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150))); + AssertExtensions.Throws(null, () => + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150), ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetColorKey_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150), type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearColorKey_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150)); + imageAttr.ClearColorKey(); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 100, 100), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearColorKey_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetColorKey(Color.FromArgb(50, 50, 50), Color.FromArgb(150, 150, 150), type); + imageAttr.ClearColorKey(type); + + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 100, 100), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearColorKey_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearColorKey()); + AssertExtensions.Throws(null, () => imageAttr.ClearColorKey(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearColorKey_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearColorKey(type)); + } + } + + public static IEnumerable SetOutputChannel_ColorChannelFlag_TestData() + { + yield return new object[] { ColorChannelFlag.ColorChannelC, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 198, 198, 198) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 108, 108, 108) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 204, 204, 204) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 207, 207, 207) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetOutputChannel_ColorChannelFlag_TestData))] + public void SetOutputChannel_Flag_Success(ColorChannelFlag flag, Color actualColor, Color expectedColor) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(flag); + + bitmap.SetPixel(0, 0, actualColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(expectedColor, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable SetOutputChannel_ColorChannelFlagType_TestData() + { + yield return new object[] { ColorChannelFlag.ColorChannelC, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 198, 198, 198) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 108, 108, 108) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 204, 204, 204) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, ColorAdjustType.Default, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 207, 207, 207) }; + yield return new object[] { ColorChannelFlag.ColorChannelC, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 198, 198, 198) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 108, 108, 108) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 204, 204, 204) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, ColorAdjustType.Bitmap, Color.FromArgb(255, 100, 100, 100), Color.FromArgb(255, 207, 207, 207) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetOutputChannel_ColorChannelFlagType_TestData))] + public void SetOutputChannel_FlagType_Success(ColorChannelFlag flag, ColorAdjustType type, Color actualColor, Color expectedColor) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(flag, type); + + bitmap.SetPixel(0, 0, actualColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(expectedColor, bitmap.GetPixel(0, 0)); + } + } + + public static IEnumerable SetOutputChannel_ColorChannelFlagTypeI_TestData() + { + yield return new object[] { ColorChannelFlag.ColorChannelC, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, ColorAdjustType.Brush, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelC, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, ColorAdjustType.Pen, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelC, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelK, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelM, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + yield return new object[] { ColorChannelFlag.ColorChannelY, ColorAdjustType.Text, Color.FromArgb(255, 100, 100, 100) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetOutputChannel_ColorChannelFlagTypeI_TestData))] + public void SetOutputChannel_FlagTypeI_Success(ColorChannelFlag flag, ColorAdjustType type, Color color) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(flag, type); + + bitmap.SetPixel(0, 0, color); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(color, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannel_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelY)); + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelY, ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetOutputChannel_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelY, type)); + } + } + + public static IEnumerable SetOutputChannel_InvalidColorChannelFlags_TestData() + { + yield return new object[] { (ColorChannelFlag)int.MinValue }; + yield return new object[] { ColorChannelFlag.ColorChannelC - 1 }; + yield return new object[] { ColorChannelFlag.ColorChannelLast }; + yield return new object[] { ColorChannelFlag.ColorChannelLast + 1 }; + yield return new object[] { (ColorChannelFlag)int.MaxValue }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetOutputChannel_InvalidColorChannelFlags_TestData))] + public void SetOutputChannel_InvalidFlags_ThrowsArgumentException(ColorChannelFlag flag) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannel(flag)); + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannel(flag, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearOutputChannel_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelC); + imageAttr.ClearOutputChannel(); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearOutputChannel_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelC, type); + imageAttr.ClearOutputChannel(type); + + bitmap.SetPixel(0, 0, _actualGreen); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_actualGreen, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearOutputChannel_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannel()); + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannel(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearOutputChannel_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannel(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_Name_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelC); + imageAttr.SetOutputChannelColorProfile(Helpers.GetTestColorProfilePath("RSWOP.icm")); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 198, 198, 198), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => + imageAttr.SetOutputChannelColorProfile(Helpers.GetTestColorProfilePath("RSWOP.icm"))); + AssertExtensions.Throws(null, () => + imageAttr.SetOutputChannelColorProfile(Helpers.GetTestColorProfilePath("RSWOP.icm"), ColorAdjustType.Default)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_Null_ThrowsArgumentNullException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(null)); + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(null, ColorAdjustType.Default)); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/22784")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_InvalidPath_ThrowsArgumentException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(string.Empty)); + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(string.Empty, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_InvalidPath_ThrowsOutOfMemoryException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile("invalidPath")); + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile("invalidPath", ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetOutputChannelColorProfile_InvalidPath_ThrowsPathTooLongException() + { + string fileNameTooLong = new string('a', short.MaxValue); + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(fileNameTooLong)); + Assert.Throws(() => imageAttr.SetOutputChannelColorProfile(fileNameTooLong, ColorAdjustType.Default)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetOutputChannelColorProfile_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetOutputChannelColorProfile("path", type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearOutputChannelColorProfile_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelC); + imageAttr.SetOutputChannelColorProfile(Helpers.GetTestColorProfilePath("RSWOP.icm")); + imageAttr.ClearOutputChannelColorProfile(); + imageAttr.ClearOutputChannel(); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 100, 100), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearOutputChannelColorProfile_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetOutputChannel(ColorChannelFlag.ColorChannelC, type); + imageAttr.SetOutputChannelColorProfile(Helpers.GetTestColorProfilePath("RSWOP.icm"), type); + imageAttr.ClearOutputChannelColorProfile(type); + imageAttr.ClearOutputChannel(type); + bitmap.SetPixel(0, 0, Color.FromArgb(255, 100, 100, 100)); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(Color.FromArgb(255, 100, 100, 100), bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearOutputChannelColorProfile_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannelColorProfile()); + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannelColorProfile(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearOutputChannelColorProfile_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearOutputChannelColorProfile(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetRemapTable_Map_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetRemapTable(_yellowToRedColorMap); + bitmap.SetPixel(0, 0, _yellowToRedColorMap[0].OldColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_yellowToRedColorMap[0].NewColor, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_TestData))] + public void SetRemapTable_MapType_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetRemapTable(_yellowToRedColorMap, type); + bitmap.SetPixel(0, 0, _yellowToRedColorMap[0].OldColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_yellowToRedColorMap[0].NewColor, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustTypeI_TestData))] + public void SetRemapTable_MapTypeI_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetRemapTable(_yellowToRedColorMap, type); + bitmap.SetPixel(0, 0, _yellowToRedColorMap[0].OldColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_yellowToRedColorMap[0].OldColor, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetRemapTable_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetRemapTable(_yellowToRedColorMap)); + AssertExtensions.Throws(null, () => imageAttr.SetRemapTable(_yellowToRedColorMap, ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void SetRemapTable_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetRemapTable(_yellowToRedColorMap, type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetRemapTable_NullMap_ThrowsNullReferenceException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetRemapTable(null, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetRemapTable_NullMapMeber_ThrowsNullReferenceException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.SetRemapTable(new ColorMap[1] { null }, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetRemapTable_EmptyMap_ThrowsArgumentException() + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.SetRemapTable(new ColorMap[0], ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearRemapTable_Success() + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetRemapTable(_yellowToRedColorMap); + imageAttr.ClearRemapTable(); + bitmap.SetPixel(0, 0, _yellowToRedColorMap[0].OldColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_yellowToRedColorMap[0].OldColor, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_AllTypesAllowed_TestData))] + public void ClearRemapTable_Type_Success(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var graphics = Graphics.FromImage(bitmap)) + using (var imageAttr = new ImageAttributes()) + { + imageAttr.SetRemapTable(_yellowToRedColorMap, type); + imageAttr.ClearRemapTable(type); + bitmap.SetPixel(0, 0, _yellowToRedColorMap[0].OldColor); + graphics.DrawImage(bitmap, _rectangle, _rectangle.X, _rectangle.Y, _rectangle.Width, _rectangle.Height, GraphicsUnit.Pixel, imageAttr); + Assert.Equal(_yellowToRedColorMap[0].OldColor, bitmap.GetPixel(0, 0)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ClearRemapTable_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.ClearRemapTable()); + AssertExtensions.Throws(null, () => imageAttr.ClearRemapTable(ColorAdjustType.Default)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void ClearRemapTable_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.ClearRemapTable(type)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetWrapMode_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + AssertExtensions.Throws(null, () => imageAttr.SetWrapMode(WrapMode.Clamp)); + AssertExtensions.Throws(null, () => imageAttr.SetWrapMode(WrapMode.Clamp, Color.Black)); + AssertExtensions.Throws(null, () => imageAttr.SetWrapMode(WrapMode.Clamp, Color.Black, true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetAdjustedPalette_Disposed_ThrowsArgumentException() + { + var imageAttr = new ImageAttributes(); + imageAttr.Dispose(); + + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + { + AssertExtensions.Throws(null, () => imageAttr.GetAdjustedPalette(bitmap.Palette, ColorAdjustType.Default)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetAdjustedPalette_NullPallete_ThrowsNullReferenceException() + { + using (var imageAttr = new ImageAttributes()) + { + Assert.Throws(() => imageAttr.GetAdjustedPalette(null, ColorAdjustType.Default)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ColorAdjustType_InvalidTypes_TestData))] + public void GetAdjustedPalette_InvalidTypes_ThrowsArgumentException(ColorAdjustType type) + { + using (var bitmap = new Bitmap(_rectangle.Width, _rectangle.Height)) + using (var imageAttr = new ImageAttributes()) + { + AssertExtensions.Throws(null, () => imageAttr.GetAdjustedPalette(bitmap.Palette, type)); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/ImageCodecInfoTests.cs b/src/System.Drawing.Common/tests/Imaging/ImageCodecInfoTests.cs new file mode 100644 index 00000000000..0a896200202 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/ImageCodecInfoTests.cs @@ -0,0 +1,218 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// (C) 2004 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class ImageCodecInfoTests + { + private const string GIF_CSID = "557cf402-1a04-11d3-9a73-0000f81ef32e"; + private const string EMF_CSID = "557cf403-1a04-11d3-9a73-0000f81ef32e"; + private const string BMP_DIB_RLE_CSID = "557cf400-1a04-11d3-9a73-0000f81ef32e"; + private const string JPG_JPEG_JPE_JFIF_CSID = "557cf401-1a04-11d3-9a73-0000f81ef32e"; + private const string PNG_CSID = "557cf406-1a04-11d3-9a73-0000f81ef32e"; + private const string ICO_CSID = "557cf407-1a04-11d3-9a73-0000f81ef32e"; + private const string WMF_CSID = "557cf404-1a04-11d3-9a73-0000f81ef32e"; + private const string TIF_CSID = "557cf405-1a04-11d3-9a73-0000f81ef32e"; + + private Hashtable decoders; + private Hashtable encoders; + + public ImageCodecInfoTests() + { + decoders = new Hashtable(); + encoders = new Hashtable(); + + foreach (ImageCodecInfo decoder in ImageCodecInfo.GetImageDecoders()) + decoders[decoder.Clsid] = decoder; + + foreach (ImageCodecInfo encoder in ImageCodecInfo.GetImageEncoders()) + encoders[encoder.Clsid] = encoder; + } + + private ImageCodecInfo GetEncoder(string clsid) + { + return (ImageCodecInfo)encoders[new Guid(clsid)]; + } + + private ImageCodecInfo GetDecoder(string clsid) + { + return (ImageCodecInfo)decoders[new Guid(clsid)]; + } + + private void CheckDecoderAndEncoder(string clsid, ImageFormat format, string CodecName, string DllName, + string FilenameExtension, ImageCodecFlags Flags, string FormatDescription, + string MimeType, int Version, int signatureLength, string mask, string pattern, string pattern2) + { + ImageCodecInfo encoder = GetEncoder(clsid); + ImageCodecInfo decoder = GetDecoder(clsid); + + if (encoder != null) + { + CheckImageCodecInfo(format, CodecName, DllName, FilenameExtension, Flags, FormatDescription, MimeType, signatureLength, mask, pattern, pattern2, encoder); + } + if (decoder != null) + { + CheckImageCodecInfo(format, CodecName, DllName, FilenameExtension, Flags, FormatDescription, MimeType, signatureLength, mask, pattern, pattern2, decoder); + } + } + + private void CheckImageCodecInfo(ImageFormat format, string CodecName, string DllName, string FilenameExtension, ImageCodecFlags Flags, string FormatDescription, string MimeType, int signatureLength, string mask, string pattern, string pattern2, ImageCodecInfo codecInfo) + { + Regex extRegex = new Regex(@"^(\*\.\w+(;(\*\.\w+))*;)?" + + Regex.Escape(FilenameExtension) + @"(;\*\.\w+(;(\*\.\w+))*)?$", + RegexOptions.IgnoreCase | RegexOptions.Singleline); + + Assert.Equal(format.Guid, codecInfo.FormatID); + Assert.Contains(CodecName, codecInfo.CodecName); + Assert.Equal(DllName, codecInfo.DllName); + Assert.Matches(extRegex, codecInfo.FilenameExtension); + Assert.Equal(Flags, codecInfo.Flags); + Assert.Contains(FormatDescription, codecInfo.FormatDescription); + Assert.Contains(MimeType, codecInfo.MimeType); + Assert.Equal(signatureLength, codecInfo.SignatureMasks.Length); + + for (int i = 0; i < signatureLength; i++) + { + Assert.Equal(mask, BitConverter.ToString(codecInfo.SignatureMasks[i])); + } + + Assert.Equal(signatureLength, codecInfo.SignaturePatterns.Length); + Assert.Equal(pattern, BitConverter.ToString(codecInfo.SignaturePatterns[0])); + if (pattern2 != null) + Assert.Equal(pattern2, BitConverter.ToString(codecInfo.SignaturePatterns[1])); + } + + public static IEnumerable CodecInfoTestData + { + get + { + yield return new object[] { WMF_CSID, ImageFormat.Wmf, + "WMF", null, "*.WMF", + ImageCodecFlags.Builtin | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "WMF", "image/x-wmf", 1, 1, "FF-FF-FF-FF", "D7-CD-C6-9A", null}; + + yield return new object[] { EMF_CSID, ImageFormat.Emf, + "EMF", null, "*.EMF", + ImageCodecFlags.Builtin | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "EMF", "image/x-emf", 1, 1, "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-FF-FF-FF-FF", + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-20-45-4D-46", null}; + + yield return new object[] { ICO_CSID, ImageFormat.Icon, + "ICO", null, "*.ICO", + ImageCodecFlags.Builtin | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "ICO", "image/x-icon", 1, 1, "FF-FF-FF-FF", "00-00-01-00", null}; + + yield return new object[] { TIF_CSID, ImageFormat.Tiff, + "TIFF", null, "*.TIF;*.TIFF", + ImageCodecFlags.Builtin | ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "TIFF", "image/tiff", 1, 2, "FF-FF", "49-49", "4D-4D" }; + + yield return new object[] { PNG_CSID, ImageFormat.Png, + "PNG", null, "*.PNG", + ImageCodecFlags.Builtin | ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "PNG", "image/png", 1, 1, "FF-FF-FF-FF-FF-FF-FF-FF", "89-50-4E-47-0D-0A-1A-0A", null }; + + yield return new object[] { JPG_JPEG_JPE_JFIF_CSID, ImageFormat.Jpeg, + "JPEG", null, "*.JPG", + ImageCodecFlags.Builtin | ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "JPEG", "image/jpeg", 1, 1, "FF-FF", "FF-D8", null}; + + yield return new object[] { GIF_CSID, ImageFormat.Gif, + "GIF", null, "*.GIF", + ImageCodecFlags.Builtin | ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "GIF", "image/gif", 1, 2, "FF-FF-FF-FF-FF-FF", "47-49-46-38-39-61", "47-49-46-38-37-61"}; + + yield return new object[] { BMP_DIB_RLE_CSID, ImageFormat.Bmp, + "BMP", null, "*.BMP", + ImageCodecFlags.Builtin | ImageCodecFlags.Encoder | ImageCodecFlags.Decoder | ImageCodecFlags.SupportBitmap, + "BMP", "image/bmp", 1, 1, "FF-FF", "42-4D", null }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GIF_CSID)] + [InlineData(EMF_CSID)] + [InlineData(BMP_DIB_RLE_CSID)] + [InlineData(JPG_JPEG_JPE_JFIF_CSID)] + [InlineData(PNG_CSID)] + [InlineData(ICO_CSID)] + [InlineData(WMF_CSID)] + [InlineData(TIF_CSID)] + public void GetDecoder_Success(string csid) + { + Assert.NotNull(GetDecoder(csid)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(GIF_CSID)] + [InlineData(BMP_DIB_RLE_CSID)] + [InlineData(JPG_JPEG_JPE_JFIF_CSID)] + [InlineData(PNG_CSID)] + [InlineData(TIF_CSID)] + public void GetEncoder_Success(string csid) + { + Assert.NotNull(GetEncoder(csid)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CountEncoders_ReturnsExcpected() + { + Assert.Equal(5, encoders.Count); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CountDecoders_ReturnsExcpected() + { + Assert.Equal(8, decoders.Count); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(CodecInfoTestData))] + public void CheckDecoderAndEncoder_ReturnsExpecetd(string clsid, ImageFormat format, string codecName, string dllName, + string fileNameExtension, ImageCodecFlags flags, string formatDescription, + string mimeType, int version, int signatureLength, string mask, string pattern, string pattern2) + { + CheckDecoderAndEncoder(clsid, format, codecName, dllName, fileNameExtension, flags, formatDescription, mimeType, version, signatureLength, mask, pattern, pattern2); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WMF_CSID)] + [InlineData(EMF_CSID)] + [InlineData(ICO_CSID)] + public void GetEncoder_NoSuchEncoding_ReturnsNull(string clsid) + { + Assert.Null(GetEncoder(clsid)); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/ImageFormatTests.cs b/src/System.Drawing.Common/tests/Imaging/ImageFormatTests.cs new file mode 100644 index 00000000000..646bccb5053 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/ImageFormatTests.cs @@ -0,0 +1,129 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.IO; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class ImageFormatTests + { + private static ImageFormat BmpImageFormat = new ImageFormat(new Guid("b96b3cab-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat EmfImageFormat = new ImageFormat(new Guid("b96b3cac-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat ExifImageFormat = new ImageFormat(new Guid("b96b3cb2-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat GifImageFormat = new ImageFormat(new Guid("b96b3cb0-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat TiffImageFormat = new ImageFormat(new Guid("b96b3cb1-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat PngImageFormat = new ImageFormat(new Guid("b96b3caf-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat MemoryBmpImageFormat = new ImageFormat(new Guid("b96b3caa-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat IconImageFormat = new ImageFormat(new Guid("b96b3cb5-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat JpegImageFormat = new ImageFormat(new Guid("b96b3cae-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat WmfImageFormat = new ImageFormat(new Guid("b96b3cad-0728-11d3-9d7b-0000f81ef32e")); + private static ImageFormat HeifImageFormat = new ImageFormat(new Guid("{b96b3cb6-0728-11d3-9d7b-0000f81ef32e}")); + private static ImageFormat WebpImageFormat = new ImageFormat(new Guid("{b96b3cb7-0728-11d3-9d7b-0000f81ef32e}")); + private static ImageFormat CustomImageFormat = new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")); + + public static IEnumerable ImageFormatGuidTestData + { + get + { + yield return new object[] { BmpImageFormat.Guid, ImageFormat.Bmp }; + yield return new object[] { EmfImageFormat.Guid, ImageFormat.Emf }; + yield return new object[] { ExifImageFormat.Guid, ImageFormat.Exif }; + yield return new object[] { GifImageFormat.Guid, ImageFormat.Gif }; + yield return new object[] { TiffImageFormat.Guid, ImageFormat.Tiff }; + yield return new object[] { PngImageFormat.Guid, ImageFormat.Png }; + yield return new object[] { MemoryBmpImageFormat.Guid, ImageFormat.MemoryBmp }; + yield return new object[] { IconImageFormat.Guid, ImageFormat.Icon }; + yield return new object[] { JpegImageFormat.Guid, ImageFormat.Jpeg }; + yield return new object[] { WmfImageFormat.Guid, ImageFormat.Wmf }; +#if NET + yield return new object[] { HeifImageFormat.Guid, ImageFormat.Heif }; + yield return new object[] { WebpImageFormat.Guid, ImageFormat.Webp }; +#endif + yield return new object[] { new Guid("48749428-316f-496a-ab30-c819a92b3137"), CustomImageFormat }; + } + } + + public static IEnumerable ImageFormatToStringTestData + { + get + { + yield return new object[] { "Bmp", ImageFormat.Bmp }; + yield return new object[] { "Emf", ImageFormat.Emf }; + yield return new object[] { "Exif", ImageFormat.Exif }; + yield return new object[] { "Gif", ImageFormat.Gif }; + yield return new object[] { "Tiff", ImageFormat.Tiff }; + yield return new object[] { "Png", ImageFormat.Png }; + yield return new object[] { "MemoryBMP", ImageFormat.MemoryBmp }; + yield return new object[] { "Icon", ImageFormat.Icon }; + yield return new object[] { "Jpeg", ImageFormat.Jpeg }; + yield return new object[] { "Wmf", ImageFormat.Wmf }; +#if NET + yield return new object[] { "Heif", ImageFormat.Heif }; + yield return new object[] { "Webp", ImageFormat.Webp }; +#endif + yield return new object[] { "[ImageFormat: 48749428-316f-496a-ab30-c819a92b3137]", CustomImageFormat }; + } + } + + public static IEnumerable ImageFromFileToStringTestData + { + get + { + yield return new object[] { Path.Combine("bitmaps", "nature24bits.gif"), "Gif" }; + yield return new object[] { Path.Combine("bitmaps", "nature24bits.jpg"), "Jpeg" }; + yield return new object[] { Path.Combine("bitmaps", "VisualPng.ico"), "Icon"}; + yield return new object[] { Path.Combine("bitmaps", "almogaver32bits.tif"), "Tiff" }; + } + } + + public static IEnumerable ImageFormatEqualsTestData + { + get + { + yield return new object[] { new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")), true }; + yield return new object[] { new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new ImageFormat(new Guid("b96b3cad-0728-11d3-9d7b-0000f81ef32e")), false }; + yield return new object[] { new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")), null, false }; + yield return new object[] { new ImageFormat(new Guid("48749428-316f-496a-ab30-c819a92b3137")), new object(), false }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ImageFormatGuidTestData))] + public void Guid_ReturnsExpected(Guid expectedGuid, ImageFormat imageFormat) + { + Assert.Equal(expectedGuid, imageFormat.Guid); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ImageFormatToStringTestData))] + public void ToString_ReturnsExpected(string expected, ImageFormat imageFormat) + { + Assert.Equal(expected, imageFormat.ToString()); + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Bug fix not in NETFX, https://github.com/dotnet/runtime/issues/20332")] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ImageFromFileToStringTestData))] + public void Image_RawFormat_ToString(string path, string expected) + { + var img = Image.FromFile(path); + Assert.Same(expected, img.RawFormat.ToString()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ImageFormatEqualsTestData))] + public void Equals_Object_ReturnsExpected(ImageFormat imageFormat, object obj, bool result) + { + Assert.Equal(result, imageFormat.Equals(obj)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHashCode_Success() + { + Guid guid = Guid.NewGuid(); + Assert.Equal(guid.GetHashCode(), new ImageFormat(guid).GetHashCode()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/MetaHeaderTests.cs b/src/System.Drawing.Common/tests/Imaging/MetaHeaderTests.cs new file mode 100644 index 00000000000..9b28296a4a3 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/MetaHeaderTests.cs @@ -0,0 +1,55 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class MetaHeaderTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + MetaHeader mh = new MetaHeader(); + Assert.Equal(0, mh.HeaderSize); + Assert.Equal(0, mh.MaxRecord); + Assert.Equal(0, mh.NoObjects); + Assert.Equal(0, mh.NoParameters); + Assert.Equal(0, mh.Size); + Assert.Equal(0, mh.Type); + Assert.Equal(0, mh.Version); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(short.MaxValue)] + [InlineData(0)] + [InlineData(short.MinValue)] + public void ShortProperties_SetValues_ReturnsExpected(short value) + { + MetaHeader mh = new MetaHeader(); + mh.HeaderSize = value; + mh.NoObjects = value; + mh.NoParameters = value; + mh.Type = value; + mh.Version = value; + Assert.Equal(value, mh.HeaderSize); + Assert.Equal(value, mh.NoObjects); + Assert.Equal(value, mh.NoParameters); + Assert.Equal(value, mh.Type); + Assert.Equal(value, mh.Version); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void IntProperties_SetValues_ReturnsExpected(int value) + { + MetaHeader mh = new MetaHeader(); + mh.Size = value; + mh.MaxRecord = value; + Assert.Equal(value, mh.Size); + Assert.Equal(value, mh.MaxRecord); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/MetafileTests.cs b/src/System.Drawing.Common/tests/Imaging/MetafileTests.cs new file mode 100644 index 00000000000..7560251c49f --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/MetafileTests.cs @@ -0,0 +1,1113 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class MetafileTests + { + private const string WmfFile = "telescope_01.wmf"; + private const string BmpFile = "bitmap_173x183_indexed_8bit.bmp"; + private readonly Rectangle _rectangle = new Rectangle(0, 0, 64, 64); + private readonly RectangleF _rectangleF = new RectangleF(0, 0, 64, 64); + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrZero_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, false)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrToWmf_ThrowsExternalException() + { + using (var metafile = new Metafile(GetPath(WmfFile))) + { + Assert.Throws(() => new Metafile(metafile.GetHenhmetafile(), false)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_String_Success() + { + using (var metafile = new Metafile(GetPath(WmfFile))) + { + AssertMetafile(metafile); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Bitmap_ThrowsExternalException() + { + Assert.Throws(() => new Metafile(GetPath(BmpFile))); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullString_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => new Metafile((string)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_InvalidPath_ThrowsExternalException() + { + Assert.Throws(() => new Metafile("fileNotExist")); + } + + public static IEnumerable InvalidPath_TestData() + { + yield return new object[] { new string('a', 261) }; + yield return new object[] { @"fileNo*-//\\#@(found" }; + yield return new object[] { string.Empty }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("bad\0name")] + [InlineData("")] + public void Ctor_InvalidPath_ThrowsArgumentException(string path) + { + AssertExtensions.Throws("path", null, () => new Metafile(path)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Ctor_Stream_Success() + { + using (FileStream stream = File.OpenRead(GetPath(WmfFile))) + using (var metafile = new Metafile(stream)) + { + AssertMetafile(metafile); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullStream_ThrowsArgumentException() + { + AssertExtensions.Throws("stream", null, () => new Metafile((Stream)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Ctor_EmptyStream_ThrowsExternalException() + { + using (var stream = new MemoryStream()) + { + Assert.Throws(() => new Metafile(stream)); + } + } + + public static IEnumerable EmfType_TestData() + { + yield return new object[] { EmfType.EmfOnly }; + yield return new object[] { EmfType.EmfPlusDual }; + yield return new object[] { EmfType.EmfPlusOnly }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_IntPtrEmfType_Success(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + } + } + + public static IEnumerable EmfType_Invalid_TestData() + { + yield return new object[] { (EmfType.EmfOnly - 1) }; + yield return new object[] { (EmfType.EmfPlusDual + 1) }; + yield return new object[] { (EmfType)int.MaxValue }; + yield return new object[] { (EmfType)int.MinValue }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_Invalid_TestData))] + public void Ctor_IntPtrInvalidEmfType_ThrowsArgumentException(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + AssertExtensions.Throws(null, () => new Metafile(g.GetHdc(), emfType)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullEmfType_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Metafile((IntPtr)null, EmfType.EmfOnly)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_ZeroPointerEmfType_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, EmfType.EmfOnly)); + } + + public static IEnumerable Description_TestData() + { + yield return new object[] { null }; + yield return new object[] { "description" }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_IntPtrEmfTypeString_Success(string description) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(InvalidPath_TestData))] + public void Ctor_ZeroPointerEmfTypeInvalidString_ThrowsArgumentException(string description) + { + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, EmfType.EmfOnly, description)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrRectangleF_Success() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangleF)) + { + AssertMetafileIsBlank(metafile); + } + } + + public static IEnumerable MetafileFrameUnit_TestData() + { + yield return new object[] { MetafileFrameUnit.Pixel }; + yield return new object[] { MetafileFrameUnit.Point }; + yield return new object[] { MetafileFrameUnit.Inch }; + yield return new object[] { MetafileFrameUnit.Document }; + yield return new object[] { MetafileFrameUnit.Millimeter }; + yield return new object[] { MetafileFrameUnit.GdiCompatible }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_TestData))] + public void Ctor_IntPtrRectangleFMetafileFrameUnit_Success(MetafileFrameUnit frameUnit) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangleF, frameUnit)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_IntPtrRectangleFMetafileFrameUnitEmfType_Success(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangleF, MetafileFrameUnit.Pixel, emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_IntPtrRectangleFMetafileFrameUnitEmfTypeString_Success(string description) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangleF, MetafileFrameUnit.Pixel, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrRectangle_Success() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangle)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_TestData))] + public void Ctor_IntPtrRectangleMetafileFrameUnit_Success(MetafileFrameUnit frameUnit) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangle, frameUnit)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_IntPtrRectangleMetafileFrameUnitEmfType_Success(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangle, MetafileFrameUnit.Pixel, emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_IntPtrRectangleMetafileFrameUnitEmfTypeString_Success(string description) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(g.GetHdc(), _rectangle, MetafileFrameUnit.Pixel, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrZeroI_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, _rectangleF)); + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, _rectangleF, MetafileFrameUnit.Pixel)); + AssertExtensions.Throws(null, () => + new Metafile(IntPtr.Zero, _rectangleF, MetafileFrameUnit.Pixel, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(IntPtr.Zero, _rectangleF, MetafileFrameUnit.Pixel, EmfType.EmfOnly, "description")); + + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, _rectangle)); + AssertExtensions.Throws(null, () => new Metafile(IntPtr.Zero, _rectangle, MetafileFrameUnit.Pixel)); + AssertExtensions.Throws(null, () => + new Metafile(IntPtr.Zero, _rectangle, MetafileFrameUnit.Pixel, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(IntPtr.Zero, _rectangle, MetafileFrameUnit.Pixel, EmfType.EmfOnly, "description")); + } + + public static IEnumerable MetafileFrameUnit_Invalid_TestData() + { + yield return new object[] { (MetafileFrameUnit.Pixel - 1) }; + yield return new object[] { (MetafileFrameUnit.GdiCompatible + 1) }; + yield return new object[] { (MetafileFrameUnit)int.MaxValue }; + yield return new object[] { (MetafileFrameUnit)int.MinValue }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_Invalid_TestData))] + public void Ctor_InvalidMetafileFrameUnit_ThrowsArgumentException(MetafileFrameUnit farameUnit) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => new Metafile(referenceHdc, _rectangleF, farameUnit)); + AssertExtensions.Throws(null, () => new Metafile(referenceHdc, _rectangleF, farameUnit, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangleF, farameUnit, EmfType.EmfOnly, "description")); + + AssertExtensions.Throws(null, () => new Metafile(referenceHdc, _rectangle, farameUnit)); + AssertExtensions.Throws(null, () => new Metafile(referenceHdc, _rectangle, farameUnit, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangle, farameUnit, EmfType.EmfOnly, "description")); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_Invalid_TestData))] + public void Ctor_InvalidEmfType_ThrowsArgumentException(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, emfType)); + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, emfType, "description")); + + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType)); + AssertExtensions.Throws(null, () => + new Metafile(referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType, "description")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_StringIntPtr_Success() + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc())) + { + AssertMetafileIsBlank(metafile); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_StringIntPtrEmfType_Success(EmfType emfType) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_StringIntPtrEmfTypeDescription_Success(string description) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), EmfType.EmfPlusDual, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfPlusDual); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrZeroII_ThrowsArgumentException() + { + string fileName = GetPath("newTestImage.wmf"); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero)); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, EmfType.EmfOnly, "description")); + DeleteFile(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_Invalid_TestData))] + public void Ctor_InvalidEmfTypeI_ThrowsArgumentException(EmfType emfType) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => new Metafile(fileName, referenceHdc, emfType)); + AssertExtensions.Throws(null, () => new Metafile(fileName, referenceHdc, emfType, "description")); + DeleteFile(fileName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullPath_ThrowsArgumentNullException() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws("path", () => new Metafile((string)null, referenceHdc)); + AssertExtensions.Throws("path", () => new Metafile((string)null, referenceHdc, EmfType.EmfOnly)); + AssertExtensions.Throws("path", () => new Metafile((string)null, referenceHdc, EmfType.EmfOnly, "description")); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("bad\0path")] + [InlineData("")] + public void Ctor_InvalidPathI_ThrowsArgumentException(string fileName) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws("path", null, () => new Metafile(fileName, referenceHdc)); + AssertExtensions.Throws("path", null, () => new Metafile(fileName, referenceHdc, EmfType.EmfOnly)); + AssertExtensions.Throws("path", null, () => new Metafile(fileName, referenceHdc, EmfType.EmfOnly, "description")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PathTooLong_ThrowsPathTooLongException() + { + string fileName = GetPath(new string('a', short.MaxValue)); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + Assert.Throws(() => new Metafile(fileName, referenceHdc)); + Assert.Throws(() => new Metafile(fileName, referenceHdc, EmfType.EmfOnly)); + Assert.Throws(() => new Metafile(fileName, referenceHdc, EmfType.EmfOnly, "description")); + DeleteFile(fileName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_StringIntPtrRectangleF_Success() + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangleF)) + { + AssertMetafileIsBlank(metafile); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_TestData))] + public void Ctor_StringIntPtrRectangleFMetafileFrameUnit_Success(MetafileFrameUnit frameUnit) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangleF, frameUnit)) + { + AssertMetafileIsBlank(metafile); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_StringIntPtrRectangleFMetafileFrameUnitEmfType_Success(EmfType emfType) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangleF, MetafileFrameUnit.GdiCompatible, emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_StringIntPtrRectangleFMetafileFrameUnitEmfTypeString_Success(string description) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile( + fileName, g.GetHdc(), _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_RectangleFEmpty_Success(string description) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile( + fileName, g.GetHdc(), new RectangleF(), MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_StringIntPtrRectangle_Success() + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangle)) + { + AssertMetafileIsBlank(metafile); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_TestData))] + public void Ctor_StringIntPtrRectangleMetafileFrameUnit_Success(MetafileFrameUnit frameUnit) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangle, frameUnit)) + { + AssertMetafileIsBlank(metafile); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_StringIntPtrRectangleMetafileFrameUnitEmfType_Success(EmfType emfType) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile(fileName, g.GetHdc(), _rectangle, MetafileFrameUnit.GdiCompatible, emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_StringIntPtrRectangleMetafileFrameUnitEmfTypeString_Success(string description) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile( + fileName, g.GetHdc(), _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Description_TestData))] + public void Ctor_RectangleEmpty_Success(string description) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var metafile = new Metafile( + fileName, g.GetHdc(), new Rectangle(), MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + Assert.True(File.Exists(fileName)); + } + + File.Delete(fileName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrZeroIII_ThrowsArgumentException() + { + string fileName = GetPath("newTestImage.wmf"); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, _rectangleF)); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, _rectangleF, MetafileFrameUnit.GdiCompatible)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, IntPtr.Zero, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, IntPtr.Zero, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, _rectangle)); + AssertExtensions.Throws(null, () => new Metafile(fileName, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + DeleteFile(fileName); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_Invalid_TestData))] + public void Ctor_InvalidFrameUnit_ThrowsArgumentException(MetafileFrameUnit frameUnit) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => new Metafile(fileName, referenceHdc, _rectangleF, frameUnit)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangleF, frameUnit, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangleF, frameUnit, EmfType.EmfOnly, "description")); + + AssertExtensions.Throws(null, () => new Metafile(fileName, referenceHdc, _rectangle, frameUnit)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangle, frameUnit, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangle, frameUnit, EmfType.EmfOnly, "description")); + DeleteFile(fileName); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_Invalid_TestData))] + public void Ctor_InvalidEmfTypeII_ThrowsArgumentException(EmfType emfType) + { + string fileName = GetPath("newTestImage.wmf"); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, emfType)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, emfType, "description")); + + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType)); + AssertExtensions.Throws(null, () => + new Metafile(fileName, referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType, "description")); + DeleteFile(fileName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullPathI_ThrowsArgumentNullException() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws("path", () => new Metafile((string)null, referenceHdc, _rectangleF)); + AssertExtensions.Throws("path", () => + new Metafile((string)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible)); + AssertExtensions.Throws("path", () => + new Metafile((string)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + AssertExtensions.Throws("path", () => + new Metafile((string)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("bad\0path")] + [InlineData("")] + public void Ctor_InvalidPathII_ThrowsArgumentException(string fileName) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws("path", null, () => new Metafile(fileName, referenceHdc, _rectangleF)); + AssertExtensions.Throws("path", null, () => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible)); + AssertExtensions.Throws("path", null, () => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + AssertExtensions.Throws("path", null, () => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_PathTooLongI_ThrowsPathTooLongException() + { + string fileName = GetPath(new string('a', 261)); + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + Assert.Throws(() => new Metafile(fileName, referenceHdc, _rectangleF)); + Assert.Throws(() => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible)); + Assert.Throws(() => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + Assert.Throws(() => + new Metafile(fileName, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + DeleteFile(fileName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Ctor_StreamIntPtrRectangle_Success() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var stream = new MemoryStream()) + using (var metafile = new Metafile(stream, g.GetHdc(), _rectangle)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [MemberData(nameof(MetafileFrameUnit_TestData))] + public void Ctor_StreamIntPtrRectangleMetafileFrameUnit_Success(MetafileFrameUnit frameUnit) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var stream = new MemoryStream()) + using (var metafile = new Metafile(stream, g.GetHdc(), _rectangle, frameUnit)) + { + AssertMetafileIsBlank(metafile); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [MemberData(nameof(EmfType_TestData))] + public void Ctor_StreamIntPtrRectangleMetafileFrameUnitEmfType_Success(EmfType emfType) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var stream = new MemoryStream()) + using (var metafile = new Metafile(stream, g.GetHdc(), _rectangle, MetafileFrameUnit.GdiCompatible, emfType)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), emfType); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [MemberData(nameof(Description_TestData))] + public void Ctor_StreamIntPtrRectangleMetafileFrameUnitEmfTypeString_Success(string description) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var stream = new MemoryStream()) + using (var metafile = new Metafile( + stream, g.GetHdc(), _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [MemberData(nameof(Description_TestData))] + public void Ctor_RectangleEmptyI_Success(string description) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + using (var stream = new MemoryStream()) + using (var metafile = new Metafile( + stream, g.GetHdc(), new Rectangle(), MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, description)) + { + AssertMetafileIsBlank(metafile); + AssertEmfType(metafile.GetMetafileHeader(), EmfType.EmfOnly); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_IntPtrZeroIV_ThrowsArgumentException() + { + using (var stream = new MemoryStream()) + { + AssertExtensions.Throws(null, () => new Metafile(stream, IntPtr.Zero, _rectangle)); + AssertExtensions.Throws(null, () => new Metafile(stream, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible)); + AssertExtensions.Throws(null, () => + new Metafile(stream, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(stream, IntPtr.Zero, _rectangle, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MetafileFrameUnit_Invalid_TestData))] + public void Ctor_InvalidFrameUnitIII_ThrowsArgumentException(MetafileFrameUnit frameUnit) + { + using (var stream = new MemoryStream()) + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => new Metafile(stream, referenceHdc, _rectangle, frameUnit)); + AssertExtensions.Throws(null, () => + new Metafile(stream, referenceHdc, _rectangle, frameUnit, EmfType.EmfOnly)); + AssertExtensions.Throws(null, () => + new Metafile(stream, referenceHdc, _rectangle, frameUnit, EmfType.EmfOnly, "description")); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/55655", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(EmfType_Invalid_TestData))] + public void Ctor_InvalidEmfTypeIII_ThrowsArgumentException(EmfType emfType) + { + using (var stream = new MemoryStream()) + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + AssertExtensions.Throws(null, () => + new Metafile(stream, referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType)); + AssertExtensions.Throws(null, () => + new Metafile(stream, referenceHdc, _rectangle, MetafileFrameUnit.GdiCompatible, emfType, "description")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullStream_ThrowsNullReferenceException() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr referenceHdc = g.GetHdc(); + Assert.Throws(() => new Metafile((Stream)null, referenceHdc, _rectangleF)); + Assert.Throws(() => new Metafile((Stream)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible)); + Assert.Throws(() => + new Metafile((Stream)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly)); + Assert.Throws(() => + new Metafile((Stream)null, referenceHdc, _rectangleF, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly, "description")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Static_GetMetafileHeader_String_ReturnsExpected() + { + MetafileHeader header = Metafile.GetMetafileHeader(GetPath(WmfFile)); + AssertMetafileHeader(header); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Static_GetMetafileHeader_IntPtr_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Metafile.GetMetafileHeader(IntPtr.Zero)); + using (var metafile = new Metafile(GetPath(WmfFile))) + { + AssertExtensions.Throws(null, () => Metafile.GetMetafileHeader(metafile.GetHenhmetafile())); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("bad\0path")] + [InlineData("")] + public void Static_GetMetafileHeader_InvalidPath_ThrowsArgumentException(string fileName) + { + AssertExtensions.Throws("path", null, () => Metafile.GetMetafileHeader(fileName)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Static_GetMetafileHeader_NullString_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => Metafile.GetMetafileHeader((string)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Static_GetMetafileHeader_Stream_ReturnsExpected() + { + using (FileStream stream = File.OpenRead(GetPath(WmfFile))) + { + MetafileHeader header = Metafile.GetMetafileHeader(stream); + AssertMetafileHeader(header); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Static_GetMetafileHeader_NullStream_ThrowsNullReferenceException() + { + Assert.Throws(() => Metafile.GetMetafileHeader((Stream)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public void Static_GetMetafileHeader_EmptyStream_ArgumentException() + { + using (var stream = new MemoryStream()) + { + AssertExtensions.Throws(null, () => Metafile.GetMetafileHeader(stream)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_ReturnsExpected() + { + using (var metafile = new Metafile(GetPath(WmfFile))) + { + MetafileHeader headerA = metafile.GetMetafileHeader(); + MetafileHeader headerB = metafile.GetMetafileHeader(); + AssertMetafileHeader(headerA); + Assert.NotSame(headerA, headerB); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_Disposed_ThrowsArgumentException() + { + var metafile = new Metafile(GetPath(WmfFile)); + metafile.Dispose(); + + AssertExtensions.Throws(null, () => metafile.GetMetafileHeader()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHenhmetafile_ReturnsExpected() + { + using (var metafile = new Metafile(GetPath(WmfFile))) + { + Assert.NotEqual(IntPtr.Zero, metafile.GetHenhmetafile()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHenhmetafile_Disposed_ThrowsArgumentException() + { + var metafile = new Metafile(GetPath(WmfFile)); + metafile.Dispose(); + + AssertExtensions.Throws(null, () => metafile.GetHenhmetafile()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PlayRecord_Disposed_ThrowsArgumentException() + { + var metafile = new Metafile(GetPath(WmfFile)); + metafile.Dispose(); + + AssertExtensions.Throws(null, () => + metafile.PlayRecord(EmfPlusRecordType.BeginContainer, 0, 1, new byte[1])); + } + + private void DeleteFile(string path) + { + if (File.Exists(path)) + { + File.Delete(path); + } + } + + private string GetPath(string fileName) + { + return Helpers.GetTestBitmapPath(fileName); + } + + private void AssertEmfType(MetafileHeader metafileHeader, EmfType emfType) + { + switch (emfType) + { + case EmfType.EmfOnly: + Assert.True(metafileHeader.IsEmf()); + break; + case EmfType.EmfPlusDual: + Assert.True(metafileHeader.IsEmfPlusDual()); + break; + case EmfType.EmfPlusOnly: + Assert.True(metafileHeader.IsEmfPlusOnly()); + break; + } + } + + private void AssertMetafileIsBlank(Metafile metafile) + { + GraphicsUnit graphicsUnit = (GraphicsUnit)int.MaxValue; + + AssertMetafileHeaderIsBlank(metafile.GetMetafileHeader()); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // This values are incorrect on libgdiplus. + Assert.Equal(new Rectangle(0, 0, 1, 1), metafile.GetBounds(ref graphicsUnit)); + Assert.Equal(GraphicsUnit.Pixel, graphicsUnit); + } + } + + private void AssertMetafileHeaderIsBlank(MetafileHeader metafileHeader) + { + Assert.Equal(new Rectangle(0, 0, 0, 0), metafileHeader.Bounds); + Assert.Equal(0, metafileHeader.MetafileSize); + } + + private void AssertMetafile(Metafile metafile) + { + GraphicsUnit graphicsUnit = (GraphicsUnit)int.MaxValue; + + AssertMetafileHeader(metafile.GetMetafileHeader()); + Assert.Equal(new Rectangle(-30, -40, 3096, 4127), metafile.GetBounds(ref graphicsUnit)); + Assert.Equal(GraphicsUnit.Pixel, graphicsUnit); + } + + private void AssertMetafileHeader(MetafileHeader header) + { + Assert.Equal(MetafileType.WmfPlaceable, header.Type); + Assert.Equal(0x300, header.Version); + Assert.Equal(new Rectangle(-30, -40, 3096, 4127), header.Bounds); + Assert.Equal(606, header.DpiX); + Assert.Equal(606, header.DpiY); + Assert.Equal(0, header.EmfPlusHeaderSize); + Assert.Equal(0, header.LogicalDpiX); + Assert.Equal(0, header.LogicalDpiY); + Assert.Equal(3474, header.MetafileSize); + Assert.NotNull(header.WmfHeader); + Assert.False(header.IsDisplay()); + Assert.False(header.IsEmf()); + Assert.False(header.IsEmfOrEmfPlus()); + Assert.False(header.IsEmfPlus()); + Assert.False(header.IsEmfPlusDual()); + Assert.False(header.IsEmfPlusOnly()); + Assert.True(header.IsWmf()); + Assert.True(header.IsWmfPlaceable()); + + Assert.Equal(9, header.WmfHeader.HeaderSize); + Assert.Equal(98, header.WmfHeader.MaxRecord); + Assert.Equal(3, header.WmfHeader.NoObjects); + Assert.Equal(0, header.WmfHeader.NoParameters); + Assert.Equal(1737, header.WmfHeader.Size); + Assert.Equal((int)MetafileType.Wmf, header.WmfHeader.Type); + Assert.Equal(0x300, header.WmfHeader.Version); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs b/src/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs new file mode 100644 index 00000000000..80928f78668 --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/PropertyItemTests.cs @@ -0,0 +1,98 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class PropertyItemTests + { + private const int PropertyTagLuminanceTable = 0x5090; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Id_Set_GetReturnsExpected(int value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Id = value; + Assert.Equal(value, item.Id); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Len_Set_GetReturnsExpected(int value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Len = value; + Assert.Equal(value, item.Len); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + public void Type_Set_GetReturnsExpected(short value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Type = value; + Assert.Equal(value, item.Type); + } + + public static IEnumerable Value_Set_TestData() + { + yield return new object[] { null }; + yield return new object[] { Array.Empty() }; + yield return new object[] { new byte[] { 1, 2, 3 } }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Value_Set_TestData))] + public void Value_Set_GetReturnsExpected(byte[] value) + { + using var bitmap = new Bitmap(Helpers.GetTestBitmapPath("nature24bits.jpg")); + PropertyItem item = bitmap.GetPropertyItem(PropertyTagLuminanceTable); + item.Value = value; + Assert.Same(value, item.Value); + } + + public static IEnumerable Properties_TestData() + { + yield return new object[] { int.MaxValue, int.MaxValue, short.MaxValue, new byte[1] { 0 } }; + yield return new object[] { int.MinValue, int.MinValue, short.MinValue, new byte[2] { 1, 1} }; + yield return new object[] { 0, 0, 0, new byte[0] }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Properties_TestData))] + public void Properties_SetValues_ReturnsExpected(int id, int len, short type, byte[] value) + { + using var image = new Bitmap(Helpers.GetTestBitmapPath("16x16_nonindexed_24bit.png")); + using Image clone = (Image)image.Clone(); + + PropertyItem[] propItems = clone.PropertyItems; + PropertyItem propItem = propItems[0]; + Assert.Equal(771, propItem.Id); + Assert.Equal(1, propItem.Len); + Assert.Equal(1, propItem.Type); + Assert.Equal(new byte[1] { 0 }, propItem.Value); + + propItem.Id = id; + propItem.Len = len; + propItem.Type = type; + propItem.Value = value; + + Assert.Equal(id, propItem.Id); + Assert.Equal(len, propItem.Len); + Assert.Equal(type, propItem.Type); + Assert.Equal(value, propItem.Value); + } + } +} diff --git a/src/System.Drawing.Common/tests/Imaging/WmfPlaceableFileHeaderTests.cs b/src/System.Drawing.Common/tests/Imaging/WmfPlaceableFileHeaderTests.cs new file mode 100644 index 00000000000..4d156ef91db --- /dev/null +++ b/src/System.Drawing.Common/tests/Imaging/WmfPlaceableFileHeaderTests.cs @@ -0,0 +1,65 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Imaging.Tests +{ + public class WmfPlaceableFileHeaderTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + WmfPlaceableFileHeader fileHeader = new WmfPlaceableFileHeader(); + Assert.Equal(0, fileHeader.BboxBottom); + Assert.Equal(0, fileHeader.BboxLeft); + Assert.Equal(0, fileHeader.BboxRight); + Assert.Equal(0, fileHeader.BboxTop); + Assert.Equal(0, fileHeader.Checksum); + Assert.Equal(0, fileHeader.Hmf); + Assert.Equal(0, fileHeader.Inch); + Assert.Equal(unchecked((int)0x9aC6CDD7), fileHeader.Key); + Assert.Equal(0, fileHeader.Reserved); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(short.MaxValue)] + [InlineData(0)] + [InlineData(short.MinValue)] + public void ShortProperties_SetValues_ReturnsExpected(short value) + { + WmfPlaceableFileHeader fileHeader = new WmfPlaceableFileHeader(); + fileHeader.BboxBottom = value; + fileHeader.BboxLeft = value; + fileHeader.BboxRight = value; + fileHeader.BboxTop = value; + fileHeader.Checksum = value; + fileHeader.Hmf = value; + fileHeader.Inch = value; + fileHeader.Key = value; + fileHeader.Reserved = value; + Assert.Equal(value, fileHeader.BboxBottom); + Assert.Equal(value, fileHeader.BboxLeft); + Assert.Equal(value, fileHeader.BboxRight); + Assert.Equal(value, fileHeader.BboxTop); + Assert.Equal(value, fileHeader.Checksum); + Assert.Equal(value, fileHeader.Hmf); + Assert.Equal(value, fileHeader.Inch); + Assert.Equal(value, fileHeader.Key); + Assert.Equal(value, fileHeader.Reserved); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(int.MaxValue)] + [InlineData(0)] + [InlineData(int.MinValue)] + public void IntProperties_SetValues_ReturnsExpected(int value) + { + WmfPlaceableFileHeader fileHeader = new WmfPlaceableFileHeader(); + fileHeader.Key = value; + fileHeader.Reserved = value; + Assert.Equal(value, fileHeader.Key); + Assert.Equal(value, fileHeader.Reserved); + } + } +} diff --git a/src/System.Drawing.Common/tests/PenTests.cs b/src/System.Drawing.Common/tests/PenTests.cs new file mode 100644 index 00000000000..797cfefaef5 --- /dev/null +++ b/src/System.Drawing.Common/tests/PenTests.cs @@ -0,0 +1,1390 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Drawing2D; +using System.Globalization; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Tests +{ + public class PenTests + { + public static IEnumerable Ctor_Brush_TestData() + { + yield return new object[] { new SolidBrush(Color.Red), PenType.SolidColor }; + yield return new object[] { new HatchBrush(HatchStyle.BackwardDiagonal, Color.Red), PenType.HatchFill }; + yield return new object[] { new LinearGradientBrush(new Point(0, 0), new Point(0, 2), Color.Purple, Color.Plum), PenType.LinearGradient }; + yield return new object[] { new TextureBrush(new Bitmap(1, 1)), PenType.TextureFill }; + yield return new object[] { new PathGradientBrush(new Point[] { new Point(1, 2), new Point(2, 3) }), PenType.PathGradient }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Brush_TestData))] + public void Ctor_Brush(T brush, PenType penType) where T : Brush + { + using (brush) + using (var pen = new Pen(brush)) + { + VerifyPen(pen, penType, expectedWidth: 1); + } + } + + public static IEnumerable Ctor_Brush_Width_TestData() + { + foreach (object[] data in Ctor_Brush_TestData()) + { + yield return new object[] { data[0], 10, data[1] }; + } + + // Issue on Non-English Windows with brush width < 1. See https://github.com/dotnet/runtime/issues/60480 + if (Environment.OSVersion.Platform != PlatformID.Win32NT || CultureInfo.InstalledUICulture.TwoLetterISOLanguageName == "en") + { + yield return new object[] { new SolidBrush(Color.Red), 0, PenType.SolidColor }; + yield return new object[] { new SolidBrush(Color.Red), -1, PenType.SolidColor }; + yield return new object[] { new SolidBrush(Color.Red), float.NegativeInfinity, PenType.SolidColor }; + } + + yield return new object[] { new SolidBrush(Color.Red), float.PositiveInfinity, PenType.SolidColor }; + yield return new object[] { new SolidBrush(Color.Red), float.NaN, PenType.SolidColor }; + yield return new object[] { new SolidBrush(Color.Red), float.MaxValue, PenType.SolidColor }; + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/60731", TestPlatforms.Windows)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Brush_Width_TestData))] + public void Ctor_Brush_Width(T brush, float width, PenType expectedPenType) where T : Brush + { + using (brush) + using (var pen = new Pen(brush, width)) + { + VerifyPen(pen, expectedPenType, width); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Brush_MakesClone() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + brush.Color = Color.Blue; + Assert.Equal(Color.FromArgb(Color.Red.ToArgb()), pen.Color); + Assert.Equal(pen.Color, Assert.IsType(pen.Brush).Color); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullBrush_ThrowsArgumentNullException() + { + AssertExtensions.Throws("brush", () => new Pen(null)); + AssertExtensions.Throws("brush", () => new Pen(null, 0)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedBrush_ThrowsArgumentException() + { + var brush = new SolidBrush(Color.Red); + brush.Dispose(); + + AssertExtensions.Throws(null, () => new Pen(brush)); + AssertExtensions.Throws(null, () => new Pen(brush, 10)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Color() + { + using (var pen = new Pen(Color.Red)) + { + VerifyPen(pen, PenType.SolidColor, 1); + Assert.Equal(Color.Red, pen.Color); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/60731", TestPlatforms.Windows)] + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(1)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NaN)] + public void Ctor_Color_Width(float width) + { + using (var pen = new Pen(Color.Red, width)) + { + VerifyPen(pen, PenType.SolidColor, width); + Assert.Equal(Color.Red, pen.Color); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PenAlignment.Center)] + [InlineData(PenAlignment.Inset)] + [InlineData(PenAlignment.Left)] + [InlineData(PenAlignment.Outset)] + [InlineData(PenAlignment.Right)] + public void Alignment_SetValid_GetReturnsExpected(PenAlignment alignment) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.Alignment = alignment; + Assert.Equal(alignment, pen.Alignment); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(PenAlignment.Center - 1)] + [InlineData(PenAlignment.Right + 1)] + public void Alignment_SetInvalid_ThrowsInvalidEnumArgumentException(PenAlignment alignment) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.Alignment = alignment); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Alignment_GetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Alignment); + AssertExtensions.Throws(null, () => pen.Alignment = PenAlignment.Center); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Brush_TestData))] + public void Brush_SetValid_GetReturnsExpected(T brush, PenType penType) where T : Brush + { + using (var pen = new Pen(Color.Red)) + { + pen.Brush = brush; + Assert.IsType(pen.Brush); + Assert.Equal(penType, pen.PenType); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Brush_SetCustomBrush_ThrowsArgumentException() + { + using (var pen = new Pen(Color.Red)) + { + AssertExtensions.Throws(null, () => pen.Brush = new SubBrush()); + } + } + + public class SubBrush : Brush + { + public override object Clone() => this; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Brush_SetNullBrush_ThrowsArgumentNullException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws("value", () => pen.Brush = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Brush_SetDisposedBrush_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + brush.Dispose(); + + AssertExtensions.Throws(null, () => pen.Brush = brush); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Brush_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Brush); + AssertExtensions.Throws(null, () => pen.Brush = brush); + } + } + + public static IEnumerable Clone_TestData() + { + using (var brush = new SolidBrush(Color.Red)) + { + yield return new object[] { new Pen(brush) }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Clone_TestData))] + public void Clone_Invoke_ReturnsExpected(Pen pen) + { + using (pen) + { + Pen clone = Assert.IsType(pen.Clone()); + Assert.NotSame(pen, clone); + + Assert.Equal(pen.Color, clone.Color); + Assert.Equal(((SolidBrush)pen.Brush).Color, ((SolidBrush)clone.Brush).Color); + Assert.Equal(pen.DashOffset, clone.DashOffset); + Assert.Equal(pen.DashStyle, clone.DashStyle); + Assert.Equal(pen.EndCap, clone.EndCap); + Assert.Equal(pen.StartCap, clone.StartCap); + Assert.Equal(pen.Width, clone.Width); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Clone()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_SolidBrush_ReturnsExpected() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.Equal(Color.FromArgb(Color.Red.ToArgb()), pen.Color); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_HatchBrush_ThrowsArgumentException() + { + using (var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.Red)) + using (var pen = new Pen(brush)) + { + ValidateInitialPenColorState(pen); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_LinearGradientBrush_ThrowsArgumentException() + { + using (var brush = new LinearGradientBrush(Point.Empty, new Point(1, 2), Color.Blue, Color.Red)) + using (var pen = new Pen(brush)) + { + ValidateInitialPenColorState(pen); + } + } + + private void ValidateInitialPenColorState(Pen pen) + { + AssertExtensions.Throws(null, () => pen.Color); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Brush_TestData))] + public void Color_Set_GetReturnsExpected(Brush brush, PenType penType) + { + _ = penType; + using (brush) + using (var pen = new Pen(brush)) + { + pen.Color = Color.Red; + Assert.Equal(Color.Red, pen.Color); + + pen.Color = Color.Red; + Assert.Equal(Color.Red, pen.Color); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_GetSetWhenDisposedWithoutBrush_Success() + { + var pen = new Pen(Color.Red); + + pen.Dispose(); + Assert.Equal(Color.Red, pen.Color); + + pen.Color = Color.Red; + Assert.Equal(Color.Red, pen.Color); + + AssertExtensions.Throws(null, () => pen.Color = Color.Black); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_GetSetWhenDisposedWithBrush_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Color); + AssertExtensions.Throws(null, () => pen.Color = Color.Red); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { 0, 0 })] + [InlineData(new float[] { 1, 1 })] + [InlineData(new float[] { float.NaN, 0 })] + public void CompoundArray_SetValidPattern_GetReturnsExpected(float[] compoundArray) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.CompoundArray = compoundArray; + Assert.Equal(compoundArray, pen.CompoundArray); + + // CompoundArray should be a clone of the original. + compoundArray[0] = 10; + Assert.NotEqual(compoundArray, pen.CompoundArray); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompoundArray_SetNullPattern_ThrowsNullReferenceException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.Throws(() => pen.CompoundArray = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompoundArray_SetEmptyPattern_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.CompoundArray = new float[0]); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { -1, 0 })] + [InlineData(new float[] { float.NegativeInfinity, 0 })] + [InlineData(new float[] { float.PositiveInfinity, 0 })] + [InlineData(new float[] { float.MaxValue, 0 })] + [InlineData(new float[] { 1024, 0 })] + [InlineData(new float[] { 2, 2 })] + [InlineData(new float[] { 1 })] + [InlineData(new float[] { 1, 2, 3 })] + public void CompoundArray_SetInvalidPattern_ThrowsArgumentException(float[] compoundArray) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.CompoundArray = compoundArray); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CompoundArray_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.CompoundArray); + AssertExtensions.Throws(null, () => pen.CompoundArray = new float[] { 1 }); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomEndCap_SetValid_GetReturnsExpected() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + using (var lineCap = new CustomLineCap(fillPath, strokePath)) + { + lineCap.BaseInset = 100; + pen.CustomEndCap = lineCap; + Assert.Equal(100, pen.CustomEndCap.BaseInset); + + // The CustomLineCap should be cloned. + lineCap.BaseInset = 10; + Assert.Equal(100, pen.CustomEndCap.BaseInset); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomEndCap_SetNull_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.CustomEndCap = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomEndCap_SetDisposedLineCap_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + { + var lineCap = new CustomLineCap(fillPath, strokePath); + lineCap.Dispose(); + + AssertExtensions.Throws(null, () => pen.CustomEndCap = lineCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomEndCap_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + using (var lineCap = new CustomLineCap(fillPath, strokePath)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.CustomEndCap); + AssertExtensions.Throws(null, () => pen.CustomEndCap = lineCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomStartCap_SetValid_GetReturnsExpected() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + using (var lineCap = new CustomLineCap(fillPath, strokePath)) + { + lineCap.BaseInset = 100; + pen.CustomStartCap = lineCap; + Assert.Equal(100, pen.CustomStartCap.BaseInset); + + // The CustomLineCap should be cloned. + lineCap.BaseInset = 10; + Assert.Equal(100, pen.CustomStartCap.BaseInset); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomStartCap_SetNull_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.CustomStartCap = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomStartCap_SetDisposedLineCap_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + { + var lineCap = new CustomLineCap(fillPath, strokePath); + lineCap.Dispose(); + + AssertExtensions.Throws(null, () => pen.CustomStartCap = lineCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CustomStartCap_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var fillPath = new GraphicsPath()) + using (var strokePath = new GraphicsPath()) + using (var lineCap = new CustomLineCap(fillPath, strokePath)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.CustomStartCap); + AssertExtensions.Throws(null, () => pen.CustomStartCap = lineCap); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(DashCap.Flat)] + [InlineData(DashCap.Round)] + [InlineData(DashCap.Triangle)] + public void DashCap_SetValid_GetReturnsExpected(DashCap dashCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.DashCap = dashCap; + Assert.Equal(dashCap, pen.DashCap); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(DashCap.Flat - 1)] + [InlineData(DashCap.Round - 1)] + [InlineData(DashCap.Triangle + 1)] + public void DashCap_SetInvalid_ThrowsInvalidEnumArgumentException(DashCap dashCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.DashCap = dashCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashCap_GetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.DashCap); + AssertExtensions.Throws(null, () => pen.DashCap = DashCap.Triangle); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(0)] + [InlineData(10)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NaN)] + public void DashOffset_Set_GetReturnsExpected(float dashOffset) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.DashOffset = dashOffset; + Assert.Equal(dashOffset, pen.DashOffset); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashOffset_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.DashOffset); + AssertExtensions.Throws(null, () => pen.DashOffset = 10); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { 1 })] + [InlineData(new float[] { 1, 1 })] + [InlineData(new float[] { float.MaxValue, float.NaN, float.PositiveInfinity })] + [InlineData(new float[] { float.MaxValue, float.NaN })] + public void DashPattern_SetValidPattern_GetReturnsExpected(float[] pattern) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.DashPattern = pattern; + Assert.Equal(pattern, pen.DashPattern); + Assert.Equal(DashStyle.Custom, pen.DashStyle); + + // DashPattern should be a clone of the original. + pattern[0] = 10; + Assert.NotEqual(pattern, pen.DashPattern); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashPattern_SetNullPattern_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.DashPattern = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashPattern_SetEmptyPattern_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.DashPattern = new float[0]); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(new float[] { -1 })] + [InlineData(new float[] { 0 })] + [InlineData(new float[] { 1, -1 })] + [InlineData(new float[] { float.NegativeInfinity })] + public void DashPattern_SetInvalidPattern_ThrowsArgumentException(float[] pattern) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.DashPattern = pattern); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashPattern_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.DashPattern); + AssertExtensions.Throws(null, () => pen.DashPattern = new float[] { 1 }); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(DashStyle.Dash, new float[] { 3, 1 })] + [InlineData(DashStyle.DashDot, new float[] { 3, 1, 1, 1 })] + [InlineData(DashStyle.DashDotDot, new float[] { 3, 1, 1, 1, 1, 1 })] + [InlineData(DashStyle.Dot, new float[] { 1, 1 })] + [InlineData(DashStyle.Solid, null)] + [InlineData(DashStyle.Custom, new float[] { 1 })] + public void DashStyle_SetValid_GetReturnsExpected(DashStyle dashStyle, float[] expectedDashPattern) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.DashStyle = dashStyle; + Assert.Equal(dashStyle, pen.DashStyle); + + if (expectedDashPattern == null) + { + Assert.Throws(() => pen.DashPattern); + } + else + { + Assert.Equal(expectedDashPattern, pen.DashPattern); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashStyle_SetCustomWithDashCount_DoeNotChangePattern() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.DashStyle = DashStyle.Dash; + pen.DashStyle = DashStyle.Custom; + + Assert.Equal(DashStyle.Custom, pen.DashStyle); + Assert.Equal(new float[] { 3, 1 }, pen.DashPattern); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(DashStyle.Solid - 1)] + [InlineData(DashStyle.Custom + 1)] + public void DashStyle_SetInvalid_ThrowsInvalidEnumArgumentException(DashStyle dashStyle) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.DashStyle = dashStyle); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DashStyle_GetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.DashStyle); + AssertExtensions.Throws(null, () => pen.DashStyle = DashStyle.Dash); + } + } + + public static IEnumerable LineCap_Valid_TestData() + { + yield return new object[] { LineCap.Flat }; + yield return new object[] { LineCap.Square }; + yield return new object[] { LineCap.Round }; + yield return new object[] { LineCap.Triangle }; + yield return new object[] { LineCap.NoAnchor }; + yield return new object[] { LineCap.SquareAnchor }; + yield return new object[] { LineCap.RoundAnchor }; + yield return new object[] { LineCap.DiamondAnchor }; + yield return new object[] { LineCap.ArrowAnchor }; + yield return new object[] { LineCap.AnchorMask }; + yield return new object[] { LineCap.Custom }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(LineCap_Valid_TestData))] + public void EndCap_SetValid_GetReturnsExpected(LineCap lineCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.EndCap = lineCap; + Assert.Equal(lineCap, pen.EndCap); + } + } + + public static IEnumerable LineCap_Invalid_TestData() + { + yield return new object[] { LineCap.Flat - 1 }; + yield return new object[] { LineCap.Triangle + 1 }; + yield return new object[] { LineCap.ArrowAnchor + 1 }; + yield return new object[] { LineCap.AnchorMask + 1 }; + yield return new object[] { LineCap.Custom + 1 }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(LineCap_Invalid_TestData))] + public void EndCap_SetInvalid_ThrowsInvalidEnumArgumentException(LineCap lineCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.EndCap = lineCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void EndCap_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.EndCap); + AssertExtensions.Throws(null, () => pen.EndCap = LineCap.ArrowAnchor); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineJoin.Bevel)] + [InlineData(LineJoin.Miter)] + [InlineData(LineJoin.MiterClipped)] + [InlineData(LineJoin.Round)] + public void LineJoin_SetValid_GetReturnsExpected(LineJoin lineJoin) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.LineJoin = lineJoin; + Assert.Equal(lineJoin, pen.LineJoin); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineJoin.Miter - 1)] + [InlineData(LineJoin.MiterClipped + 1)] + public void LineJoin_SetInvalid_ThrowsInvalidEnumArgumentException(LineJoin lineJoin) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.LineJoin = lineJoin); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LineJoin_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.LineJoin); + AssertExtensions.Throws(null, () => pen.LineJoin = LineJoin.Miter); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, 1)] + [InlineData(0, 1)] + [InlineData(10, 10)] + [InlineData(float.NegativeInfinity, 1)] + [InlineData(float.PositiveInfinity, float.PositiveInfinity)] + [InlineData(float.NaN, float.NaN)] + public void MiterLimit_Set_GetReturnsExpected(float miterLimit, float expectedMiterLimit) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.MiterLimit = miterLimit; + Assert.Equal(expectedMiterLimit, pen.MiterLimit); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MiterLimit_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.MiterLimit); + AssertExtensions.Throws(null, () => pen.MiterLimit = 10); + } + } + + public static IEnumerable MultiplyTransform_TestData() + { + yield return new object[] { new Matrix(), new Matrix(1, 2, 3, 4, 5, 6), MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), new Matrix(1, 2, 3, 4, 5, 6), MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), new Matrix(2, 3, 4, 5, 6, 7), MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), new Matrix(2, 3, 4, 5, 6, 7), MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MultiplyTransform_TestData))] + public void MultiplyTransform_Matrix_SetsTransformToExpected(Matrix originalTransform, Matrix matrix, MatrixOrder matrixOrder) + { + try + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Multiply(matrix, matrixOrder); + pen.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + Pen clone = (Pen)pen.Clone(); + clone.MultiplyTransform(matrix); + Assert.Equal(expected, clone.Transform); + } + + pen.MultiplyTransform(matrix, matrixOrder); + Assert.Equal(expected, pen.Transform); + } + } + finally + { + originalTransform.Dispose(); + matrix.Dispose(); + } + } + + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Null refs on .NET Framework")] + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NullMatrix_ThrowsArgumentNullException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.Throws(() => pen.MultiplyTransform(null)); + Assert.Throws(() => pen.MultiplyTransform(null, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NotInvertibleMatrix_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => pen.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => pen.MultiplyTransform(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_DisposedMatrix_Nop() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + pen.Transform = transform; + + var matrix = new Matrix(); + matrix.Dispose(); + + pen.MultiplyTransform(matrix); + pen.MultiplyTransform(matrix, MatrixOrder.Append); + + Assert.Equal(transform, pen.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void MultiplyTransform_InvalidOrder_Nop(MatrixOrder matrixOrder) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix()) + { + pen.Transform = transform; + + pen.MultiplyTransform(matrix, matrixOrder); + Assert.Equal(transform, pen.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => pen.MultiplyTransform(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Invoke_SetsTransformToZero() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix()) + { + pen.Transform = transform; + pen.ResetTransform(); + Assert.Equal(matrix, pen.Transform); + + pen.ResetTransform(); + Assert.Equal(matrix, pen.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.ResetTransform()); + } + } + public static IEnumerable RotateTransform_TestData() + { + yield return new object[] { new Matrix(), 90, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 90, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 360, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 360, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -45, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -45, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(RotateTransform_TestData))] + public void RotateTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float angle, MatrixOrder matrixOrder) + { + using (originalTransform) + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Rotate(angle, matrixOrder); + pen.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + Pen clone = (Pen)pen.Clone(); + clone.RotateTransform(angle); + Assert.Equal(expected, clone.Transform); + } + + pen.RotateTransform(angle, matrixOrder); + Assert.Equal(expected, pen.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void RotateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.RotateTransform(10, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.RotateTransform(1)); + AssertExtensions.Throws(null, () => pen.RotateTransform(1, MatrixOrder.Prepend)); + } + } + + public static IEnumerable ScaleTransform_TestData() + { + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ScaleTransform_TestData))] + public void ScaleTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float scaleX, float scaleY, MatrixOrder matrixOrder) + { + using (originalTransform) + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Scale(scaleX, scaleY, matrixOrder); + pen.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + Pen clone = (Pen)pen.Clone(); + clone.ScaleTransform(scaleX, scaleY); + Assert.Equal(expected, clone.Transform); + } + + pen.ScaleTransform(scaleX, scaleY, matrixOrder); + Assert.Equal(expected, pen.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void ScaleTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.ScaleTransform(1, 2, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.ScaleTransform(1, 2)); + AssertExtensions.Throws(null, () => pen.ScaleTransform(1, 2, MatrixOrder.Prepend)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(LineCap.Flat, LineCap.Round, DashCap.Triangle)] + [InlineData(LineCap.Flat - 1, LineCap.Flat - 1, DashCap.Flat - 1)] + [InlineData((LineCap)int.MaxValue, (LineCap)int.MaxValue, (DashCap)int.MaxValue)] + public void SetLineCap_Invoke_Success(LineCap startCap, LineCap endCap, DashCap dashCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + // Make sure that if DashCap is invalid then it is reset to Flat. + if (Enum.IsDefined(typeof(DashCap), dashCap)) + { + pen.DashCap = DashCap.Round; + } + + pen.SetLineCap(startCap, endCap, dashCap); + Assert.Equal(startCap, pen.StartCap); + Assert.Equal(endCap, pen.EndCap); + Assert.Equal(Enum.IsDefined(typeof(DashCap), dashCap) ? dashCap : DashCap.Flat, pen.DashCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetLineCap_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.SetLineCap(LineCap.AnchorMask, LineCap.ArrowAnchor, DashCap.Flat)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(LineCap_Valid_TestData))] + public void StartCap_SetValid_GetReturnsExpected(LineCap lineCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.StartCap = lineCap; + Assert.Equal(lineCap, pen.StartCap); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(LineCap_Invalid_TestData))] + public void StartCap_SetInvalid_ThrowsInvalidEnumArgumentException(LineCap lineCap) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + Assert.ThrowsAny(() => pen.StartCap = lineCap); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StartCap_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.StartCap); + AssertExtensions.Throws(null, () => pen.StartCap = LineCap.ArrowAnchor); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetValid_GetReturnsExpected() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var matrix = new Matrix(1, 2, 3, 4, 5,6 )) + using (var expected = new Matrix(1, 2, 3, 4, 5, 6)) + { + pen.Transform = matrix; + Assert.Equal(matrix, pen.Transform); + + // The Matrix should be cloned. + matrix.Translate(1, 2); + Assert.Equal(expected, pen.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNull_ThrowsArgumentNullException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws("value", () => pen.Transform = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNotInvertible_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => pen.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetDisposedLineCap_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + var matrix = new Matrix(); + matrix.Dispose(); + + AssertExtensions.Throws(null, () => pen.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Transform); + AssertExtensions.Throws(null, () => pen.Transform = matrix); + } + } + + public static IEnumerable TranslateTransform_TestData() + { + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TranslateTransform_TestData))] + public void TranslateTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float dX, float dY, MatrixOrder matrixOrder) + { + using (originalTransform) + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Translate(dX, dY, matrixOrder); + pen.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + Pen clone = (Pen)pen.Clone(); + clone.TranslateTransform(dX, dY); + Assert.Equal(expected, clone.Transform); + } + + pen.TranslateTransform(dX, dY, matrixOrder); + Assert.Equal(expected, pen.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void TranslateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + AssertExtensions.Throws(null, () => pen.TranslateTransform(1, 2, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Disposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + using (var matrix = new Matrix()) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.TranslateTransform(1, 2)); + AssertExtensions.Throws(null, () => pen.TranslateTransform(1, 2, MatrixOrder.Prepend)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-10)] + [InlineData(0)] + [InlineData(10)] + [InlineData(float.NegativeInfinity)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NaN)] + public void Width_Set_GetReturnsExpected(float value) + { + using (var brush = new SolidBrush(Color.Red)) + using (var pen = new Pen(brush)) + { + pen.Width = value; + Assert.Equal(value, pen.Width); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Width_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var brush = new SolidBrush(Color.Red)) + { + var pen = new Pen(brush); + pen.Dispose(); + + AssertExtensions.Throws(null, () => pen.Width); + AssertExtensions.Throws(null, () => pen.Width = 10); + } + } + + private void VerifyPen(Pen pen, PenType expectedPenType, float expectedWidth) where T : Brush + { + Assert.Equal(PenAlignment.Center, pen.Alignment); + + Assert.IsType(pen.Brush); + + Assert.Empty(pen.CompoundArray); + + AssertExtensions.Throws(null, () => pen.CustomEndCap); + AssertExtensions.Throws(null, () => pen.CustomStartCap); + + Assert.Equal(DashCap.Flat, pen.DashCap); + Assert.Equal(0, pen.DashOffset); + + Assert.Throws(() => pen.DashPattern); + + Assert.Equal(DashStyle.Solid, pen.DashStyle); + Assert.Equal(LineCap.Flat, pen.EndCap); + Assert.Equal(LineJoin.Miter, pen.LineJoin); + Assert.Equal(10, pen.MiterLimit); + Assert.Equal(expectedPenType, pen.PenType); + Assert.Equal(LineCap.Flat, pen.StartCap); + + using (var matrix = new Matrix()) + { + Assert.Equal(new Matrix(), pen.Transform); + } + Assert.Equal(expectedWidth, pen.Width); + } + } +} diff --git a/src/System.Drawing.Common/tests/PensTests.cs b/src/System.Drawing.Common/tests/PensTests.cs new file mode 100644 index 00000000000..58f872c5933 --- /dev/null +++ b/src/System.Drawing.Common/tests/PensTests.cs @@ -0,0 +1,172 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Reflection; +using Xunit; + +namespace System.Drawing.Tests +{ + public class PensTests + { + public static IEnumerable Pens_TestData() + { + yield return Pen(() => Pens.AliceBlue, Color.AliceBlue); + yield return Pen(() => Pens.AntiqueWhite, Color.AntiqueWhite); + yield return Pen(() => Pens.Aqua, Color.Aqua); + yield return Pen(() => Pens.Aquamarine, Color.Aquamarine); + yield return Pen(() => Pens.Azure, Color.Azure); + yield return Pen(() => Pens.Beige, Color.Beige); + yield return Pen(() => Pens.Bisque, Color.Bisque); + yield return Pen(() => Pens.Black, Color.Black); + yield return Pen(() => Pens.BlanchedAlmond, Color.BlanchedAlmond); + yield return Pen(() => Pens.Blue, Color.Blue); + yield return Pen(() => Pens.BlueViolet, Color.BlueViolet); + yield return Pen(() => Pens.Brown, Color.Brown); + yield return Pen(() => Pens.BurlyWood, Color.BurlyWood); + yield return Pen(() => Pens.CadetBlue, Color.CadetBlue); + yield return Pen(() => Pens.Chartreuse, Color.Chartreuse); + yield return Pen(() => Pens.Chocolate, Color.Chocolate); + yield return Pen(() => Pens.Coral, Color.Coral); + yield return Pen(() => Pens.CornflowerBlue, Color.CornflowerBlue); + yield return Pen(() => Pens.Cornsilk, Color.Cornsilk); + yield return Pen(() => Pens.Crimson, Color.Crimson); + yield return Pen(() => Pens.Cyan, Color.Cyan); + yield return Pen(() => Pens.DarkBlue, Color.DarkBlue); + yield return Pen(() => Pens.DarkCyan, Color.DarkCyan); + yield return Pen(() => Pens.DarkGoldenrod, Color.DarkGoldenrod); + yield return Pen(() => Pens.DarkGray, Color.DarkGray); + yield return Pen(() => Pens.DarkGreen, Color.DarkGreen); + yield return Pen(() => Pens.DarkKhaki, Color.DarkKhaki); + yield return Pen(() => Pens.DarkMagenta, Color.DarkMagenta); + yield return Pen(() => Pens.DarkOliveGreen, Color.DarkOliveGreen); + yield return Pen(() => Pens.DarkOrange, Color.DarkOrange); + yield return Pen(() => Pens.DarkOrchid, Color.DarkOrchid); + yield return Pen(() => Pens.DarkRed, Color.DarkRed); + yield return Pen(() => Pens.DarkSalmon, Color.DarkSalmon); + yield return Pen(() => Pens.DarkSeaGreen, Color.DarkSeaGreen); + yield return Pen(() => Pens.DarkSlateBlue, Color.DarkSlateBlue); + yield return Pen(() => Pens.DarkSlateGray, Color.DarkSlateGray); + yield return Pen(() => Pens.DarkTurquoise, Color.DarkTurquoise); + yield return Pen(() => Pens.DarkViolet, Color.DarkViolet); + yield return Pen(() => Pens.DeepPink, Color.DeepPink); + yield return Pen(() => Pens.DeepSkyBlue, Color.DeepSkyBlue); + yield return Pen(() => Pens.DimGray, Color.DimGray); + yield return Pen(() => Pens.DodgerBlue, Color.DodgerBlue); + yield return Pen(() => Pens.Firebrick, Color.Firebrick); + yield return Pen(() => Pens.FloralWhite, Color.FloralWhite); + yield return Pen(() => Pens.ForestGreen, Color.ForestGreen); + yield return Pen(() => Pens.Fuchsia, Color.Fuchsia); + yield return Pen(() => Pens.Gainsboro, Color.Gainsboro); + yield return Pen(() => Pens.GhostWhite, Color.GhostWhite); + yield return Pen(() => Pens.Gold, Color.Gold); + yield return Pen(() => Pens.Goldenrod, Color.Goldenrod); + yield return Pen(() => Pens.Gray, Color.Gray); + yield return Pen(() => Pens.Green, Color.Green); + yield return Pen(() => Pens.GreenYellow, Color.GreenYellow); + yield return Pen(() => Pens.Honeydew, Color.Honeydew); + yield return Pen(() => Pens.HotPink, Color.HotPink); + yield return Pen(() => Pens.IndianRed, Color.IndianRed); + yield return Pen(() => Pens.Indigo, Color.Indigo); + yield return Pen(() => Pens.Ivory, Color.Ivory); + yield return Pen(() => Pens.Khaki, Color.Khaki); + yield return Pen(() => Pens.Lavender, Color.Lavender); + yield return Pen(() => Pens.LavenderBlush, Color.LavenderBlush); + yield return Pen(() => Pens.LawnGreen, Color.LawnGreen); + yield return Pen(() => Pens.LemonChiffon, Color.LemonChiffon); + yield return Pen(() => Pens.LightBlue, Color.LightBlue); + yield return Pen(() => Pens.LightCoral, Color.LightCoral); + yield return Pen(() => Pens.LightCyan, Color.LightCyan); + yield return Pen(() => Pens.LightGoldenrodYellow, Color.LightGoldenrodYellow); + yield return Pen(() => Pens.LightGray, Color.LightGray); + yield return Pen(() => Pens.LightGreen, Color.LightGreen); + yield return Pen(() => Pens.LightPink, Color.LightPink); + yield return Pen(() => Pens.LightSalmon, Color.LightSalmon); + yield return Pen(() => Pens.LightSeaGreen, Color.LightSeaGreen); + yield return Pen(() => Pens.LightSkyBlue, Color.LightSkyBlue); + yield return Pen(() => Pens.LightSlateGray, Color.LightSlateGray); + yield return Pen(() => Pens.LightSteelBlue, Color.LightSteelBlue); + yield return Pen(() => Pens.LightYellow, Color.LightYellow); + yield return Pen(() => Pens.Lime, Color.Lime); + yield return Pen(() => Pens.LimeGreen, Color.LimeGreen); + yield return Pen(() => Pens.Linen, Color.Linen); + yield return Pen(() => Pens.Magenta, Color.Magenta); + yield return Pen(() => Pens.Maroon, Color.Maroon); + yield return Pen(() => Pens.MediumAquamarine, Color.MediumAquamarine); + yield return Pen(() => Pens.MediumBlue, Color.MediumBlue); + yield return Pen(() => Pens.MediumOrchid, Color.MediumOrchid); + yield return Pen(() => Pens.MediumPurple, Color.MediumPurple); + yield return Pen(() => Pens.MediumSeaGreen, Color.MediumSeaGreen); + yield return Pen(() => Pens.MediumSlateBlue, Color.MediumSlateBlue); + yield return Pen(() => Pens.MediumSpringGreen, Color.MediumSpringGreen); + yield return Pen(() => Pens.MediumTurquoise, Color.MediumTurquoise); + yield return Pen(() => Pens.MediumVioletRed, Color.MediumVioletRed); + yield return Pen(() => Pens.MidnightBlue, Color.MidnightBlue); + yield return Pen(() => Pens.MintCream, Color.MintCream); + yield return Pen(() => Pens.MistyRose, Color.MistyRose); + yield return Pen(() => Pens.Moccasin, Color.Moccasin); + yield return Pen(() => Pens.NavajoWhite, Color.NavajoWhite); + yield return Pen(() => Pens.Navy, Color.Navy); + yield return Pen(() => Pens.OldLace, Color.OldLace); + yield return Pen(() => Pens.Olive, Color.Olive); + yield return Pen(() => Pens.OliveDrab, Color.OliveDrab); + yield return Pen(() => Pens.Orange, Color.Orange); + yield return Pen(() => Pens.OrangeRed, Color.OrangeRed); + yield return Pen(() => Pens.Orchid, Color.Orchid); + yield return Pen(() => Pens.PaleGoldenrod, Color.PaleGoldenrod); + yield return Pen(() => Pens.PaleGreen, Color.PaleGreen); + yield return Pen(() => Pens.PaleTurquoise, Color.PaleTurquoise); + yield return Pen(() => Pens.PaleVioletRed, Color.PaleVioletRed); + yield return Pen(() => Pens.PapayaWhip, Color.PapayaWhip); + yield return Pen(() => Pens.PeachPuff, Color.PeachPuff); + yield return Pen(() => Pens.Peru, Color.Peru); + yield return Pen(() => Pens.Pink, Color.Pink); + yield return Pen(() => Pens.Plum, Color.Plum); + yield return Pen(() => Pens.PowderBlue, Color.PowderBlue); + yield return Pen(() => Pens.Purple, Color.Purple); + yield return Pen(() => Pens.Red, Color.Red); + yield return Pen(() => Pens.RosyBrown, Color.RosyBrown); + yield return Pen(() => Pens.RoyalBlue, Color.RoyalBlue); + yield return Pen(() => Pens.SaddleBrown, Color.SaddleBrown); + yield return Pen(() => Pens.Salmon, Color.Salmon); + yield return Pen(() => Pens.SandyBrown, Color.SandyBrown); + yield return Pen(() => Pens.SeaGreen, Color.SeaGreen); + yield return Pen(() => Pens.SeaShell, Color.SeaShell); + yield return Pen(() => Pens.Sienna, Color.Sienna); + yield return Pen(() => Pens.Silver, Color.Silver); + yield return Pen(() => Pens.SkyBlue, Color.SkyBlue); + yield return Pen(() => Pens.SlateBlue, Color.SlateBlue); + yield return Pen(() => Pens.SlateGray, Color.SlateGray); + yield return Pen(() => Pens.Snow, Color.Snow); + yield return Pen(() => Pens.SpringGreen, Color.SpringGreen); + yield return Pen(() => Pens.SteelBlue, Color.SteelBlue); + yield return Pen(() => Pens.Tan, Color.Tan); + yield return Pen(() => Pens.Teal, Color.Teal); + yield return Pen(() => Pens.Thistle, Color.Thistle); + yield return Pen(() => Pens.Tomato, Color.Tomato); + yield return Pen(() => Pens.Transparent, Color.Transparent); + yield return Pen(() => Pens.Turquoise, Color.Turquoise); + yield return Pen(() => Pens.Violet, Color.Violet); + yield return Pen(() => Pens.Wheat, Color.Wheat); + yield return Pen(() => Pens.White, Color.White); + yield return Pen(() => Pens.WhiteSmoke, Color.WhiteSmoke); + yield return Pen(() => Pens.Yellow, Color.Yellow); + yield return Pen(() => Pens.YellowGreen, Color.YellowGreen); + } + + public static object[] Pen(Func getPen, Color expectedColor) => new object[] { getPen, expectedColor }; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Pens_TestData))] + public void Pens_Get_ReturnsExpected(Func getPen, Color expectedColor) + { + Pen pen = getPen(); + Assert.Equal(expectedColor, pen.Color); + Assert.Equal(PenType.SolidColor, pen.PenType); + AssertExtensions.Throws(null, () => pen.Color = Color.AliceBlue); + + Assert.Same(pen, getPen()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/MarginsTests.cs b/src/System.Drawing.Common/tests/Printing/MarginsTests.cs new file mode 100644 index 00000000000..e7c9f5b88c3 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/MarginsTests.cs @@ -0,0 +1,250 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Authors: +// Sebastien Pouliot +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class MarginsTests + { + [Fact] + public void Ctor_Default() + { + var margins = new Margins(); + Assert.Equal(100, margins.Left); + Assert.Equal(100, margins.Top); + Assert.Equal(100, margins.Right); + Assert.Equal(100, margins.Bottom); + } + + [Theory] + [InlineData(int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue)] + [InlineData(0, 1, 2, 3)] + [InlineData(0, 0, 0, 0)] + public void Ctor_Bounds(int left, int right, int top, int bottom) + { + var margins = new Margins(left, right, top, bottom); + Assert.Equal(left, margins.Left); + Assert.Equal(right, margins.Right); + Assert.Equal(top, margins.Top); + Assert.Equal(bottom, margins.Bottom); + } + + [Fact] + public void Ctor_NegativeLeft_ThrowsArgumentOutOfRangeException() + { + AssertExtensions.Throws("left", null, () => new Margins(-1, 2, 3, 4)); + } + + [Fact] + public void Ctor_NegativeRight_ThrowsArgumentOutOfRangeException() + { + AssertExtensions.Throws("right", null, () => new Margins(1, -1, 3, 4)); + } + + [Fact] + public void Ctor_NegativeTop_ThrowsArgumentOutOfRangeException() + { + AssertExtensions.Throws("top", null, () => new Margins(1, 2, -1, 4)); + } + + [Fact] + public void Ctor_NegativeBottom_ThrowsArgumentOutOfRangeException() + { + AssertExtensions.Throws("bottom", null, () => new Margins(1, 2, 3, -1)); + } + + public static IEnumerable Equals_Object_TestData() + { + var margins = new Margins(1, 2, 3, 4); + yield return new object[] { margins, margins, true }; + yield return new object[] { margins, new Margins(1, 2, 3, 4), true }; + yield return new object[] { margins, new Margins(2, 2, 3, 4), false }; + yield return new object[] { margins, new Margins(1, 3, 3, 4), false }; + yield return new object[] { margins, new Margins(1, 2, 4, 4), false }; + yield return new object[] { margins, new Margins(1, 2, 3, 5), false }; + + yield return new object[] { margins, new object(), false }; + yield return new object[] { margins, null, false }; + } + + [Theory] + [MemberData(nameof(Equals_Object_TestData))] + public void Equals_InvokeObject_ReturnsExpected(Margins margins, object obj, bool expected) + { + Assert.Equal(expected, margins.Equals(obj)); + if (obj is Margins) + { + Assert.Equal(expected, margins.GetHashCode().Equals(obj.GetHashCode())); + } + } + + public static IEnumerable Equals_Margin_TestData() + { + var margins = new Margins(1, 2, 3, 4); + yield return new object[] { margins, margins, true }; + yield return new object[] { margins, new Margins(1, 2, 3, 4), true }; + yield return new object[] { margins, new Margins(2, 2, 3, 4), false }; + yield return new object[] { margins, new Margins(1, 3, 3, 4), false }; + yield return new object[] { margins, new Margins(1, 2, 4, 4), false }; + yield return new object[] { margins, new Margins(1, 2, 3, 5), false }; + + yield return new object[] { null, null, true }; + yield return new object[] { null, new Margins(1, 2, 3, 4), false }; + yield return new object[] { new Margins(1, 2, 3, 4), null, false }; + } + + [Theory] + [MemberData(nameof(Equals_Margin_TestData))] + public void Equals_InvokeMargin_ReturnsExpected(Margins margins1, Margins margins2, bool expected) + { + Assert.Equal(expected, margins1 == margins2); + Assert.Equal(!expected, margins1 != margins2); + } + + public static IEnumerable ToString_TestData() + { + yield return new object[] { new Margins(), "[Margins Left=100 Right=100 Top=100 Bottom=100]" }; + yield return new object[] { new Margins(1, 2, 3, 4), "[Margins Left=1 Right=2 Top=3 Bottom=4]" }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public void ToString_Invoke_ReturnsExpected(Margins margins, string expected) + { + Assert.Equal(expected, margins.ToString()); + } + + [Fact] + public void Clone_Invoke_ReturnsExpected() + { + var margins = new Margins(1, 2, 3, 4); + Margins clonedMargins = Assert.IsType(margins.Clone()); + Assert.NotSame(margins, clonedMargins); + Assert.Equal(1, clonedMargins.Left); + Assert.Equal(2, clonedMargins.Right); + Assert.Equal(3, clonedMargins.Top); + Assert.Equal(4, clonedMargins.Bottom); + } + + public static IEnumerable Bounds_Set_TestData() + { + yield return new object[] { 0 }; + yield return new object[] { 10 }; + yield return new object[] { int.MaxValue }; + } + + [Theory] + [MemberData(nameof(Bounds_Set_TestData))] + public void Left_Set_GetReturnsExpected(int value) + { + var margins = new Margins + { + Left = value + }; + Assert.Equal(value, margins.Left); + + // Set same. + margins.Left = value; + Assert.Equal(value, margins.Left); + } + + [Fact] + public void Left_SetNegative_ThrowsArgumentOutOfRangeException() + { + var margins = new Margins(); + AssertExtensions.Throws("value", null, () => margins.Left = -1); + } + + [Theory] + [MemberData(nameof(Bounds_Set_TestData))] + public void Right_Set_GetReturnsExpected(int value) + { + var margins = new Margins + { + Right = value + }; + Assert.Equal(value, margins.Right); + + // Set same. + margins.Right = value; + Assert.Equal(value, margins.Right); + } + + [Fact] + public void Right_SetNegative_ThrowsArgumentOutOfRangeException() + { + var margins = new Margins(); + AssertExtensions.Throws("value", null, () => margins.Right = -1); + } + + [Theory] + [MemberData(nameof(Bounds_Set_TestData))] + public void Top_Set_GetReturnsExpected(int value) + { + var margins = new Margins + { + Top = value + }; + Assert.Equal(value, margins.Top); + + // Set same. + margins.Top = value; + Assert.Equal(value, margins.Top); + } + + [Fact] + public void Top_SetNegative_ThrowsArgumentOutOfRangeException() + { + var margins = new Margins(); + AssertExtensions.Throws("value", null, () => margins.Top = -1); + } + + [Theory] + [MemberData(nameof(Bounds_Set_TestData))] + public void Bottom_Set_GetReturnsExpected(int value) + { + var margins = new Margins + { + Bottom = value + }; + Assert.Equal(value, margins.Bottom); + + // Set same. + margins.Bottom = value; + Assert.Equal(value, margins.Bottom); + } + + [Fact] + public void Bottom_SetNegative_ThrowsArgumentOutOfRangeException() + { + var margins = new Margins(); + AssertExtensions.Throws("value", null, () => margins.Bottom = -1); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PageSettingsTests.cs b/src/System.Drawing.Common/tests/Printing/PageSettingsTests.cs new file mode 100644 index 00000000000..b91a9741b59 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PageSettingsTests.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Author: +// +// Jordi Mas i Hernandez (jordi@ximian.com) +// + +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PageSettingsTests + { + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void Clone_Success() + { + PageSettings ps = new PageSettings(); + ps.Color = false; + ps.Landscape = true; + ps.Margins = new Margins(120, 130, 140, 150); + ps.PaperSize = new PaperSize("My Custom Size", 222, 333); + PageSettings clone = (PageSettings)ps.Clone(); + + Assert.Equal(ps.Color, clone.Color); + Assert.Equal(ps.Landscape, clone.Landscape); + Assert.Equal(ps.Margins, clone.Margins); + Assert.Same(ps.PrinterSettings, clone.PrinterSettings); + + // PaperSize + Assert.Equal(ps.PaperSize.PaperName, clone.PaperSize.PaperName); + Assert.Equal(ps.PaperSize.Width, clone.PaperSize.Width); + Assert.Equal(ps.PaperSize.Height, clone.PaperSize.Height); + Assert.Equal(ps.PaperSize.Kind, clone.PaperSize.Kind); + + // PrinterResolution + Assert.Equal(ps.PrinterResolution.X, clone.PrinterResolution.X); + Assert.Equal(ps.PrinterResolution.Y, clone.PrinterResolution.Y); + Assert.Equal(ps.PrinterResolution.Kind, clone.PrinterResolution.Kind); + + // PaperSource + Assert.Equal(ps.PaperSource.Kind, clone.PaperSource.Kind); + Assert.Equal(ps.PaperSource.SourceName, clone.PaperSource.SourceName); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PaperSizeTests.cs b/src/System.Drawing.Common/tests/Printing/PaperSizeTests.cs new file mode 100644 index 00000000000..3f948021d7a --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PaperSizeTests.cs @@ -0,0 +1,234 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Copyright (C) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Author: +// Andy Hume +// + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PaperSizeTests + { + [Fact] + public void Ctor_Default() + { + var size = new PaperSize(); + Assert.Equal(PaperKind.Custom, size.Kind); + Assert.Equal(0, size.Height); + Assert.Empty(size.PaperName); + Assert.Equal(0, size.RawKind); + Assert.Equal(0, size.Width); + } + + [Theory] + [InlineData(null, -1, -2)] + [InlineData("", 0, 0)] + [InlineData("name", 100, 200)] + public void Ctor_String_Int_Int(string name, int width, int height) + { + var size = new PaperSize(name, width, height); + Assert.Equal(PaperKind.Custom, size.Kind); + Assert.Equal(height, size.Height); + Assert.Equal(name, size.PaperName); + Assert.Equal(0, size.RawKind); + Assert.Equal(width, size.Width); + } + + public static IEnumerable RawKind_TestData() + { + yield return new object[] { (int)PaperKind.A4 }; + yield return new object[] { (int)PaperKind.JapaneseEnvelopeKakuNumber3 }; + yield return new object[] { (int)PaperKind.Custom }; + yield return new object[] { 999999 }; + yield return new object[] { int.MaxValue }; + yield return new object[] { -1 }; + yield return new object[] { int.MinValue }; + yield return new object[] { 2 }; + yield return new object[] { 1 + (int)PaperKind.PrcEnvelopeNumber10Rotated }; + } + + public static IEnumerable Height_Set_TestData() + { + foreach (object[] testData in RawKind_TestData()) + { + yield return new object[] { testData[0], -1 }; + yield return new object[] { testData[0], 0 }; + yield return new object[] { testData[0], 100 }; + } + } + + [Theory] + [MemberData(nameof(Height_Set_TestData))] + public void Height_Set_GetReturnsExpected(int rawKind, int value) + { + var size = new PaperSize + { + RawKind = rawKind, + Height = value + }; + Assert.Equal(value, size.Height); + + // Set same. + size.Height = value; + Assert.Equal(value, size.Height); + } + + public static IEnumerable NonCustomRawKind_TestData() + { + yield return new object[] { (int)PaperKind.A4 }; + yield return new object[] { (int)PaperKind.JapaneseEnvelopeKakuNumber3 }; + yield return new object[] { 999999 }; + yield return new object[] { int.MaxValue }; + yield return new object[] { -1 }; + yield return new object[] { int.MinValue }; + yield return new object[] { 1 + (int)PaperKind.PrcEnvelopeNumber10Rotated }; + } + + [Theory] + [MemberData(nameof(NonCustomRawKind_TestData))] + public void Height_SetNonCustomKindConstructor_ThrowsArgumentException(int rawKind) + { + var size = new PaperSize("name", 100, 200) + { + RawKind = rawKind + }; + AssertExtensions.Throws("value", null, () => size.Height = 1); + } + + public static IEnumerable PaperName_Set_TestData() + { + foreach (object[] testData in RawKind_TestData()) + { + yield return new object[] { testData[0], null }; + yield return new object[] { testData[0], string.Empty }; + yield return new object[] { testData[0], "name" }; + } + } + + [Theory] + [MemberData(nameof(PaperName_Set_TestData))] + public void PaperName_Set_GetReturnsExpected(int rawKind, string value) + { + var size = new PaperSize + { + RawKind = rawKind, + PaperName = value + }; + Assert.Equal(value, size.PaperName); + + // Set same. + size.PaperName = value; + Assert.Equal(value, size.PaperName); + } + + [Theory] + [MemberData(nameof(NonCustomRawKind_TestData))] + public void PaperName_SetNonCustomKindConstructor_ThrowsArgumentException(int rawKind) + { + var size = new PaperSize("name", 100, 200) + { + RawKind = rawKind + }; + AssertExtensions.Throws("value", null, () => size.PaperName = "name"); + } + + [Theory] + [InlineData((int)PaperKind.Custom, PaperKind.Custom)] + [InlineData((int)PaperKind.A4, PaperKind.A4)] + [InlineData((int)PaperKind.JapaneseEnvelopeKakuNumber3, PaperKind.JapaneseEnvelopeKakuNumber3)] + [InlineData(999999, PaperKind.Custom)] + [InlineData(int.MaxValue, PaperKind.Custom)] + [InlineData(1 + (int)PaperKind.PrcEnvelopeNumber10Rotated, PaperKind.Custom)] + [InlineData(-1, (PaperKind)(-1))] + [InlineData(int.MinValue, (PaperKind)int.MinValue)] + public void RawKind_Set_GetReturnsExpected(int value, PaperKind expectedKind) + { + var size = new PaperSize + { + RawKind = value + }; + Assert.Equal(value, size.RawKind); + Assert.Equal(expectedKind, size.Kind); + + // Set same. + size.RawKind = value; + Assert.Equal(value, size.RawKind); + Assert.Equal(expectedKind, size.Kind); + } + + public static IEnumerable Width_Set_TestData() + { + foreach (object[] testData in RawKind_TestData()) + { + yield return new object[] { testData[0], -1 }; + yield return new object[] { testData[0], 0 }; + yield return new object[] { testData[0], 100 }; + } + } + + [Theory] + [MemberData(nameof(Width_Set_TestData))] + public void Width_Set_GetReturnsExpected(int rawKind, int value) + { + var size = new PaperSize + { + RawKind = rawKind, + Width = value + }; + Assert.Equal(value, size.Width); + + // Set same. + size.Width = value; + Assert.Equal(value, size.Width); + } + + [Theory] + [MemberData(nameof(NonCustomRawKind_TestData))] + public void Width_SetNonCustomKindConstructor_ThrowsArgumentException(int rawKind) + { + var size = new PaperSize("name", 100, 200) + { + RawKind = rawKind + }; + AssertExtensions.Throws("value", null, () => size.Width = 1); + } + + public static IEnumerable ToString_TestData() + { + yield return new object[] { new PaperSize(), "[PaperSize Kind=Custom Height=0 Width=0]" }; + yield return new object[] { new PaperSize("name", 1, 2), "[PaperSize name Kind=Custom Height=2 Width=1]" }; + yield return new object[] { new PaperSize("name", -1, -2), "[PaperSize name Kind=Custom Height=-2 Width=-1]" }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public void ToString_Invoke_ReturnsExpected(PaperSize size, string expected) + { + Assert.Equal(expected, size.ToString()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PaperSourceTests.cs b/src/System.Drawing.Common/tests/Printing/PaperSourceTests.cs new file mode 100644 index 00000000000..8949515d0b8 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PaperSourceTests.cs @@ -0,0 +1,95 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2009 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Author: +// Andy Hume +// + +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PaperSourceTests + { + [Fact] + public void Ctor_Default() + { + var source = new PaperSource(); + Assert.Equal(PaperSourceKind.Custom, source.Kind); + Assert.Equal((int)PaperSourceKind.Custom, source.RawKind); + Assert.Empty(source.SourceName); + } + + [Theory] + [InlineData((int)PaperSourceKind.Custom, PaperSourceKind.Custom)] + [InlineData((int)PaperSourceKind.Upper, PaperSourceKind.Upper)] + [InlineData((int)PaperSourceKind.TractorFeed, PaperSourceKind.TractorFeed)] + [InlineData((int)PaperSourceKind.SmallFormat, PaperSourceKind.SmallFormat)] + [InlineData((int)PaperSourceKind.Middle, PaperSourceKind.Middle)] + [InlineData((int)PaperSourceKind.ManualFeed, PaperSourceKind.ManualFeed)] + [InlineData((int)PaperSourceKind.Manual, PaperSourceKind.Manual)] + [InlineData((int)PaperSourceKind.Lower, PaperSourceKind.Lower)] + [InlineData((int)PaperSourceKind.LargeFormat, PaperSourceKind.LargeFormat)] + [InlineData((int)PaperSourceKind.LargeCapacity, PaperSourceKind.LargeCapacity)] + [InlineData((int)PaperSourceKind.FormSource, PaperSourceKind.FormSource)] + [InlineData((int)PaperSourceKind.Envelope, PaperSourceKind.Envelope)] + [InlineData((int)PaperSourceKind.Cassette, PaperSourceKind.Cassette)] + [InlineData((int)PaperSourceKind.AutomaticFeed, PaperSourceKind.AutomaticFeed)] + [InlineData(int.MaxValue, PaperSourceKind.Custom)] + [InlineData(int.MinValue, (PaperSourceKind)int.MinValue)] + [InlineData(0, (PaperSourceKind)0)] + [InlineData(256, PaperSourceKind.Custom)] + public void RawKind_Set_GetReturnsExpected(int value, PaperSourceKind expectedKind) + { + var source = new PaperSource + { + RawKind = value + }; + Assert.Equal(value, source.RawKind); + Assert.Equal(expectedKind, source.Kind); + + // Set same. + source.RawKind = value; + Assert.Equal(value, source.RawKind); + Assert.Equal(expectedKind, source.Kind); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("sourceName")] + public void SourceName_Set_GetReturnsExpected(string value) + { + var source = new PaperSource + { + SourceName = value + }; + Assert.Equal(value, source.SourceName); + + // Set same. + source.SourceName = value; + Assert.Equal(value, source.SourceName); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs b/src/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs new file mode 100644 index 00000000000..df90ef59fc1 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PreviewPrintControllerTests.cs @@ -0,0 +1,145 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PreviewPrintControllerTests + { + [Fact] + public void Ctor_Default() + { + var controller = new PreviewPrintController(); + Assert.True(controller.IsPreview); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void OnStartPage_InvokeWithPrint_ReturnsNull() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnStartPrint(document, new PrintEventArgs()); + + var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, new PageSettings()); + Assert.NotNull(controller.OnStartPage(document, printEventArgs)); + + // Call OnEndPage. + controller.OnEndPage(document, printEventArgs); + + // Call EndPrint. + controller.OnEndPrint(document, new PrintEventArgs()); + } + } + + [Fact] + public void OnStartPage_InvokeNullDocument_ThrowsNullReferenceException() + { + var controller = new PreviewPrintController(); + var e = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null); + Assert.Throws(() => controller.OnStartPage(null, e)); + } + + [Fact] + public void OnStartPage_InvokeNullEventArgs_ThrowsNullReferenceException() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + Assert.Throws(() => controller.OnStartPage(document, null)); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void OnStartPage_InvokeNullEventArgsPageSettings_ReturnsNull() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnStartPrint(document, new PrintEventArgs()); + + var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null); + Assert.Throws(() => controller.OnStartPage(document, printEventArgs)); + } + } + + [Fact] + public void OnStartPage_PrintNotStarted_ThrowsNullReferenceException() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + var e = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null); + Assert.Throws(() => controller.OnStartPage(document, e)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Fixed a NullReferenceException")] + public void OnEndPage_InvokeWithoutStarting_Nop() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnEndPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null)); + controller.OnEndPage(null, null); + } + } + + public static IEnumerable PrintEventArgs_TestData() + { + yield return new object[] { null }; + yield return new object[] { new PrintEventArgs() }; + } + + [ConditionalTheory(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + [MemberData(nameof(PrintEventArgs_TestData))] + public void OnStartPrint_InvokeWithDocument_Success(PrintEventArgs e) + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnStartPrint(document, e); + + // Call OnEndPrint + controller.OnEndPrint(document, e); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void OnStartPrint_InvokeMultipleTimes_Success() + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnStartPrint(document, new PrintEventArgs()); + controller.OnStartPrint(document, new PrintEventArgs()); + + // Call OnEndPrint + controller.OnEndPrint(document, new PrintEventArgs()); + } + } + + [Fact] + public void OnStartPrint_InvokeNullDocument_ThrowsNullReferenceException() + { + var controller = new PreviewPrintController(); + Assert.Throws(() => controller.OnStartPrint(null, new PrintEventArgs())); + } + + [Theory] + [MemberData(nameof(PrintEventArgs_TestData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Fixed a NullReferenceException")] + public void OnEndPrint_InvokeWithoutStarting_Nop(PrintEventArgs e) + { + using (var document = new PrintDocument()) + { + var controller = new PreviewPrintController(); + controller.OnEndPrint(document, e); + controller.OnEndPrint(null, e); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PrintControllerTests.cs b/src/System.Drawing.Common/tests/Printing/PrintControllerTests.cs new file mode 100644 index 00000000000..6544f51cd7d --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PrintControllerTests.cs @@ -0,0 +1,117 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PrintControllerTests + { + [Fact] + public void Ctor_Default() + { + var controller = new SubPrintController(); + Assert.False(controller.IsPreview); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void OnStartPage_InvokeWithPrint_ReturnsNull() + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + controller.OnStartPrint(document, new PrintEventArgs()); + + var printEventArgs = new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null); + Assert.Null(controller.OnStartPage(document, printEventArgs)); + + // Call OnEndPage. + controller.OnEndPage(document, printEventArgs); + + // Call EndPrint. + controller.OnEndPrint(document, new PrintEventArgs()); + } + } + + [Fact] + public void OnStartPage_Invoke_ReturnsNull() + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + Assert.Null(controller.OnStartPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null))); + Assert.Null(controller.OnStartPage(null, null)); + } + } + + [Fact] + public void OnEndPage_InvokeWithoutStarting_Nop() + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + controller.OnEndPage(document, new PrintPageEventArgs(null, Rectangle.Empty, Rectangle.Empty, null)); + controller.OnEndPage(null, null); + } + } + + public static IEnumerable PrintEventArgs_TestData() + { + yield return new object[] { null }; + yield return new object[] { new PrintEventArgs() }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(PrintEventArgs_TestData))] + public void OnStartPrint_InvokeWithDocument_Success(PrintEventArgs e) + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + controller.OnStartPrint(document, e); + + // Call OnEndPrint + controller.OnEndPrint(document, e); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(PrintEventArgs_TestData))] + public void OnStartPrint_InvokeWithDocumentSeveralTimes_Success(PrintEventArgs e) + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + controller.OnStartPrint(document, e); + controller.OnStartPrint(document, e); + + // Call OnEndPrint + controller.OnEndPrint(document, e); + } + } + + [Fact] + public void OnStartPrint_InvokeNullDocument_ThrowsNullReferenceException() + { + var controller = new SubPrintController(); + Assert.Throws(() => controller.OnStartPrint(null, new PrintEventArgs())); + } + + [Theory] + [MemberData(nameof(PrintEventArgs_TestData))] + public void OnEndPrint_InvokeWithoutStarting_Nop(PrintEventArgs e) + { + using (var document = new PrintDocument()) + { + var controller = new SubPrintController(); + controller.OnEndPrint(document, e); + controller.OnEndPrint(null, e); + } + } + + private class SubPrintController : PrintController + { + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PrintDocumentTests.cs b/src/System.Drawing.Common/tests/Printing/PrintDocumentTests.cs new file mode 100644 index 00000000000..7a96b12c432 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PrintDocumentTests.cs @@ -0,0 +1,321 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2005-2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System.IO; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PrintDocumentTests : FileCleanupTestBase + { + private readonly PageSettings _pageSettings = new PageSettings() + { + PaperSize = new PaperSize() + { + RawKind = (int)PaperKind.A3 + } + }; + + [ConditionalFact(Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void Ctor_Default_Success() + { + using (var document = new PrintDocument()) + { + Assert.Equal("document", document.DocumentName); + Assert.False(document.OriginAtMargins); + AssertDefaultPageSettings(document.DefaultPageSettings); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void DefaultPageSettings_SetValue_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + document.DefaultPageSettings = null; + Assert.IsAssignableFrom(document.DefaultPageSettings); + + document.DefaultPageSettings = _pageSettings; + Assert.Equal(_pageSettings.PaperSize.Kind, _pageSettings.PaperSize.Kind); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/30221")] + public void DefaultPageSettings_Null_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + document.DefaultPageSettings = null; + AssertDefaultPageSettings(document.DefaultPageSettings); + } + } + + [Theory] + [InlineData("")] + [InlineData("newDocument")] + public void DocumentName_SetValue_ReturnsExpected(string documentName) + { + using (var document = new PrintDocument()) + { + document.DocumentName = documentName; + Assert.Equal(documentName, document.DocumentName); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DocumentName_Null_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + document.DocumentName = null; + Assert.Equal(string.Empty, document.DocumentName); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void OriginAtMargins_SetValue_ReturnsExpected(bool originAtMargins) + { + using (var document = new PrintDocument()) + { + document.OriginAtMargins = originAtMargins; + Assert.Equal(originAtMargins, document.OriginAtMargins); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void PrintController_SetValue_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + document.PrintController = null; + Assert.NotNull(document.PrintController); + + var printController = new StandardPrintController(); + document.PrintController = printController; + Assert.Same(printController, document.PrintController); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void PrinterSettings_SetValue_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + document.PrinterSettings = null; + Assert.IsAssignableFrom(document.PrinterSettings); + + var printerSettings = new PrinterSettings(); + document.PrinterSettings = printerSettings; + Assert.Same(printerSettings, document.PrinterSettings); + Assert.Equal( + document.PrinterSettings.DefaultPageSettings.PaperSize.Kind, + document.DefaultPageSettings.PaperSize.Kind); + + document.DefaultPageSettings = _pageSettings; + document.PrinterSettings = printerSettings; + Assert.Equal( + _pageSettings.PaperSize.Kind, + document.DefaultPageSettings.PaperSize.Kind); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void BeginPrint_SetValue_ReturnsExpected() + { + bool flag = false; + var beginPrintHandler = new PrintEventHandler((sender, e) => flag = true); + + using (var document = new PrintDocument()) + { + document.PrintController = new TestPrintController(); + document.BeginPrint += beginPrintHandler; + document.Print(); + Assert.True(flag); + + flag = false; + document.BeginPrint -= beginPrintHandler; + document.Print(); + Assert.False(flag); + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/26428")] + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void EndPrint_SetValue_ReturnsExpected() + { + bool flag = false; + var endPrintHandler = new PrintEventHandler((sender, e) => flag = true); + + using (var document = new PrintDocument()) + { + document.PrintController = new TestPrintController(); + document.EndPrint += endPrintHandler; + document.Print(); + Assert.True(flag); + + flag = false; + document.EndPrint -= endPrintHandler; + document.Print(); + Assert.False(flag); + } + } + + [ConditionalFact(nameof(CanPrintToPdf))] + public void Print_DefaultPrintController_Success() + { + bool endPrintCalled = false; + var endPrintHandler = new PrintEventHandler((sender, e) => endPrintCalled = true); + using (var document = new PrintDocument()) + { + document.PrinterSettings.PrinterName = PrintToPdfPrinterName; + document.PrinterSettings.PrintFileName = GetTestFilePath(); + document.PrinterSettings.PrintToFile = true; + document.EndPrint += endPrintHandler; + document.Print(); + document.EndPrint -= endPrintHandler; + } + + // File may not have finished saving to disk when Print returns, + // so we check for EndPrint being called instead of file existence. + Assert.True(endPrintCalled); + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/26428")] + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void PrintPage_SetValue_ReturnsExpected() + { + bool flag = false; + var printPageHandler = new PrintPageEventHandler((sender, e) => flag = true); + + using (var document = new PrintDocument()) + { + document.PrintController = new TestPrintController(); + document.PrintPage += printPageHandler; + document.Print(); + Assert.True(flag); + + flag = false; + document.PrintPage -= printPageHandler; + document.Print(); + Assert.False(flag); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void QueryPageSettings_SetValue_ReturnsExpected() + { + bool flag = false; + var queryPageSettingsHandler = new QueryPageSettingsEventHandler((sender, e) => flag = true); + + using (var document = new PrintDocument()) + { + document.PrintController = new TestPrintController(); + document.QueryPageSettings += queryPageSettingsHandler; + document.Print(); + Assert.True(flag); + + flag = false; + document.QueryPageSettings -= queryPageSettingsHandler; + document.Print(); + Assert.False(flag); + } + } + + [Fact] + public void ToString_ReturnsExpected() + { + using (var document = new PrintDocument()) + { + var expected = string.Format("[PrintDocument {0}]", document.DocumentName); + Assert.Equal(expected, document.ToString()); + } + } + + private void AssertDefaultPageSettings(PageSettings pageSettings) + { + // A4 and Letter are both common default sizes for systems to have. + switch (pageSettings.PaperSize.Kind) + { + case PaperKind.A4: + Assert.Equal(new Rectangle(0, 0, 827, 1169), pageSettings.Bounds); + break; + + case PaperKind.Letter: + Assert.Equal(new Rectangle(0, 0, 850, 1100), pageSettings.Bounds); + break; + } + + Assert.True(Enum.IsDefined(typeof(PrinterResolutionKind), pageSettings.PrinterResolution.Kind)); + Assert.True(pageSettings.PrinterSettings.IsDefaultPrinter); + } + + private const string PrintToPdfPrinterName = "Microsoft Print to PDF"; + private static bool CanPrintToPdf() + { + if (!PlatformDetection.IsWindows || !PlatformDetection.IsDrawingSupported) + return false; + + foreach (string name in PrinterSettings.InstalledPrinters) + { + if (name == PrintToPdfPrinterName) + { + return true; + } + } + + return false; + } + + private class TestPrintController : PrintController + { + public override Graphics OnStartPage(PrintDocument document, PrintPageEventArgs e) + { + using (var bitmap = new Bitmap(20, 20)) + { + return Graphics.FromImage(bitmap); + } + } + + public override void OnStartPrint(PrintDocument document, PrintEventArgs e) + { + base.OnStartPrint(document, e); + } + + public override void OnEndPrint(PrintDocument document, PrintEventArgs e) + { + base.OnEndPrint(document, e); + } + + public override void OnEndPage(PrintDocument document, PrintPageEventArgs e) + { + base.OnEndPage(document, e); + e.Graphics.Dispose(); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PrinterResolutionTests.cs b/src/System.Drawing.Common/tests/Printing/PrinterResolutionTests.cs new file mode 100644 index 00000000000..5cbb6d415f8 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PrinterResolutionTests.cs @@ -0,0 +1,102 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PrinterResolutionTests + { + [Fact] + public void Ctor_Default() + { + var resolution = new PrinterResolution(); + Assert.Equal(PrinterResolutionKind.Custom, resolution.Kind); + Assert.Equal(0, resolution.X); + Assert.Equal(0, resolution.Y); + } + + [Theory] + [InlineData(int.MaxValue)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void X_Value_ReturnsExpected(int value) + { + var resolution = new PrinterResolution + { + X = value + }; + Assert.Equal(value, resolution.X); + + // Set same. + resolution.X = value; + Assert.Equal(value, resolution.X); + } + + [Theory] + [InlineData(int.MaxValue)] + [InlineData(1)] + [InlineData(0)] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void Y_Value_ReturnsExpected(int value) + { + var resolution = new PrinterResolution + { + Y = value + }; + Assert.Equal(value, resolution.Y); + + // Set same. + resolution.Y = value; + Assert.Equal(value, resolution.Y); + } + + [Theory] + [InlineData(PrinterResolutionKind.Custom)] + [InlineData(PrinterResolutionKind.Draft)] + [InlineData(PrinterResolutionKind.High)] + [InlineData(PrinterResolutionKind.Low)] + [InlineData(PrinterResolutionKind.Medium)] + public void Kind_Set_GetReturnsExpected(PrinterResolutionKind value) + { + var resolution = new PrinterResolution + { + Kind = value + }; + Assert.Equal(value, resolution.Kind); + + // Set same. + resolution.Kind = value; + Assert.Equal(value, resolution.Kind); + } + + [Theory] + [InlineData(PrinterResolutionKind.Custom + 1)] + [InlineData(PrinterResolutionKind.High - 1)] + public void Kind_SetInvalid_ThrowsInvalidEnumArgumentException(PrinterResolutionKind value) + { + var resolution = new PrinterResolution(); + Assert.Throws("value", () => resolution.Kind = value); + } + + public static IEnumerable ToString_TestData() + { + yield return new object[] { new PrinterResolution(), "[PrinterResolution X=0 Y=0]" }; + yield return new object[] { new PrinterResolution { X = -1, Y = -2}, "[PrinterResolution X=-1 Y=-2]" }; + yield return new object[] { new PrinterResolution { Kind = PrinterResolutionKind.High }, "[PrinterResolution High]" }; + yield return new object[] { new PrinterResolution { X = 1, Y = 2, Kind = PrinterResolutionKind.High }, "[PrinterResolution High]" }; + } + + [Theory] + [MemberData(nameof(ToString_TestData))] + public void ToString_Invoke_ReturnsExpected(PrinterResolution resolution, string expected) + { + Assert.Equal(expected, resolution.ToString()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PrinterSettingsTests.cs b/src/System.Drawing.Common/tests/Printing/PrinterSettingsTests.cs new file mode 100644 index 00000000000..91f90397f8b --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PrinterSettingsTests.cs @@ -0,0 +1,631 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Authors: +// Sebastien Pouliot +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Imaging; +using System.Globalization; +using System.Linq; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PrinterSettingsTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default_Success() + { + var printerSettings = new PrinterSettings(); + Assert.NotNull(printerSettings.DefaultPageSettings); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void CanDuplex_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + bool canDuplex = printerSettings.CanDuplex; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Copies_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + int copies = printerSettings.Copies; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(short.MaxValue)] + public void Copies_SetValue_ReturnsExpected(short copies) + { + var printerSettings = new PrinterSettings() + { + Copies = copies + }; + + Assert.Equal(copies, printerSettings.Copies); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1)] + [InlineData(short.MinValue)] + public void Copies_SetValue_ThrowsArgumentException(short copies) + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.Copies = copies); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void Collate_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + bool collate = printerSettings.Collate; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Collate_SetValue_ReturnsExpected() + { + var printerSettings = new PrinterSettings() + { + Collate = false + }; + + Assert.False(printerSettings.Collate); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DefaultPageSettings_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.NotNull(printerSettings.DefaultPageSettings); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(Duplex.Simplex)] + [InlineData(Duplex.Vertical)] + [InlineData(Duplex.Horizontal)] + public void Duplex_SetValue_ReturnsExpected(Duplex duplex) + { + var printerSettings = new PrinterSettings() + { + Duplex = duplex + }; + + Assert.Equal(duplex, printerSettings.Duplex); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(Duplex.Default - 1)] + [InlineData(Duplex.Horizontal + 1)] + [InlineData((Duplex)int.MaxValue)] + [InlineData((Duplex)int.MinValue)] + public void Duplex_Invalid_ThrowsInvalidEnumArgumentException(Duplex duplex) + { + var printerSettings = new PrinterSettings(); + Assert.ThrowsAny(() => printerSettings.Duplex = duplex); + } + + [Fact] + public void FromPage_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + + Assert.Equal(0, printerSettings.FromPage); + } + + [Theory] + [InlineData(1)] + [InlineData(int.MaxValue)] + public void FromPage_SetValue_ReturnsExpected(int pageNumber) + { + var printerSettings = new PrinterSettings() + { + FromPage = pageNumber + }; + + Assert.Equal(pageNumber, printerSettings.FromPage); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void FromPage_Invalid_ThrowsArgumentException(int pageNumber) + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.FromPage = pageNumber); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void Static_InstalledPrinters_ReturnsExpected() + { + Assert.NotNull(PrinterSettings.InstalledPrinters); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsDefaultPrinter_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.True(printerSettings.IsDefaultPrinter); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void IsPlotter_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.False(printerSettings.IsPlotter); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsValid_ReturnsExpected() + { + var printerSettings = new PrinterSettings() + { + PrinterName = "Invalid Printer" + }; + + Assert.False(printerSettings.IsValid); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void LandscapeAngle_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + int[] validValues = new[] { -90, 0, 90, 270 }; + Assert.True(validValues.Contains(printerSettings.LandscapeAngle), $"PrinterSettings.LandscapeAngle ({printerSettings.LandscapeAngle}) must be 0, 90, or 270 degrees."); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void MaximumCopies_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.True(printerSettings.MaximumCopies >= 0, $"PrinterSettings.MaximumCopies ({printerSettings.MaximumCopies}) should not be negative."); + } + + [Fact] + public void MaximumPage_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + + Assert.Equal(9999, printerSettings.MaximumPage); + } + + [Theory] + [InlineData(20)] + [InlineData(int.MaxValue)] + public void MaximumPage_SetValue_ReturnsExpected(int maximumPage) + { + var printerSettings = new PrinterSettings() + { + MaximumPage = maximumPage + }; + + Assert.Equal(maximumPage, printerSettings.MaximumPage); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void MaximumPage_Invalid_ThrowsArgumentException(int maximumPage) + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.MaximumPage = maximumPage); + } + + [Fact] + public void MinimumPage_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.Equal(0, printerSettings.MinimumPage); + } + + [Theory] + [InlineData(20)] + [InlineData(int.MaxValue)] + public void MinimumPage_SetValue_ReturnsExpected(int minimumPage) + { + var printerSettings = new PrinterSettings() + { + MinimumPage = minimumPage + }; + + Assert.Equal(minimumPage, printerSettings.MinimumPage); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void MinimumPage_Invalid_ThrowsArgumentException(int minimumPage) + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.MinimumPage = minimumPage); + } + + [Fact] + public void PrintFileName_SetValue_ReturnsExpected() + { + var printFileName = "fileName"; + var printerSettings = new PrinterSettings() + { + PrintFileName = printFileName + }; + + Assert.Equal(printFileName, printerSettings.PrintFileName); + } + + [Fact] + public void PrintFileName_Null_ThrowsArgumentNullException() + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.PrintFileName = null); + } + + [Fact] + public void PrintFileName_Empty_ThrowsArgumentNullException() + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(string.Empty, () => printerSettings.PrintFileName = string.Empty); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void PaperSizes_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.NotNull(printerSettings.PaperSizes); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void PaperSources_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.NotNull(printerSettings.PaperSources); + } + + [Theory] + [InlineData(PrintRange.AllPages)] + [InlineData(PrintRange.CurrentPage)] + [InlineData(PrintRange.Selection)] + [InlineData(PrintRange.SomePages)] + public void PrintRange_SetValue_ReturnsExpected(PrintRange printRange) + { + var printerSettings = new PrinterSettings() + { + PrintRange = printRange + }; + + Assert.Equal(printRange, printerSettings.PrintRange); + } + + [Theory] + [InlineData(PrintRange.AllPages - 1)] + [InlineData(PrintRange.SomePages + 1)] + [InlineData((PrintRange)int.MaxValue)] + [InlineData((PrintRange)int.MinValue)] + public void PrintRange_Invalid_ThrowsInvalidEnumArgumentException(PrintRange printRange) + { + var printerSettings = new PrinterSettings(); + Assert.ThrowsAny(() => printerSettings.PrintRange = printRange); + } + + [Fact] + public void PrintToFile_SetValue_ReturnsExpected() + { + var printToFile = true; + var printerSettings = new PrinterSettings() + { + PrintToFile = printToFile + }; + + Assert.Equal(printToFile, printerSettings.PrintToFile); + } + + [Theory] + [InlineData("")] + [InlineData("My printer")] + public void PrinterName_SetValue_ReturnsExpected(string printerName) + { + var printerSettings = new PrinterSettings() + { + PrinterName = printerName + }; + + Assert.Equal(printerName, printerSettings.PrinterName); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void PrinterName_Null_ReturnsExpected() + { + var printerSettings = new PrinterSettings() + { + PrinterName = null + }; + + Assert.NotNull(printerSettings.PrinterName); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + public void PrinterResolutions_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + Assert.NotNull(printerSettings.PrinterResolutions); + } + + public static IEnumerable IsDirectPrintingSupported_ImageFormatSupported_TestData() + { + yield return new object[] { ImageFormat.Jpeg }; + yield return new object[] { ImageFormat.Png }; + } + + [ConditionalTheory(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported)] + [MemberData(nameof(IsDirectPrintingSupported_ImageFormatSupported_TestData))] + public void IsDirectPrintingSupported_ImageFormatSupported_ReturnsExpected(ImageFormat imageFormat) + { + var printerSettings = new PrinterSettings(); + bool supported = printerSettings.IsDirectPrintingSupported(imageFormat); + } + + public static IEnumerable IsDirectPrintingSupported_ImageFormatNotSupported_TestData() + { + yield return new object[] { ImageFormat.Emf }; + yield return new object[] { ImageFormat.Exif }; + yield return new object[] { ImageFormat.Gif }; + yield return new object[] { ImageFormat.Icon }; + yield return new object[] { ImageFormat.MemoryBmp }; + yield return new object[] { ImageFormat.Tiff }; + yield return new object[] { ImageFormat.Wmf }; + yield return new object[] { ImageFormat.Bmp }; + } + + [Theory] + [MemberData(nameof(IsDirectPrintingSupported_ImageFormatNotSupported_TestData))] + public void IsDirectPrintingSupported_ImageFormatNotSupported_ReturnsExpected(ImageFormat imageFormat) + { + var printerSettings = new PrinterSettings(); + Assert.False(printerSettings.IsDirectPrintingSupported(imageFormat)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsDirectPrintingSupported_ImageNotSupported_ReturnsExpected() + { + using (var bitmap = new Bitmap(10, 10)) + { + var printerSettings = new PrinterSettings(); + Assert.False(printerSettings.IsDirectPrintingSupported(bitmap)); + } + } + + [ConditionalFact(typeof(PrinterSettingsTests), nameof(CanTestSetHdevmode_IntPtr_Success))] + public void SupportsColor_ReturnsExpected() + { + // XPS and PDF printers support color. + // docs.microsoft.com/en-us/windows-hardware/drivers/print/improved-color-printing + var printerSettings = new PrinterSettings() { PrinterName = GetNameOfTestPrinterSuitableForDevModeTesting() }; + Assert.True(printerSettings.SupportsColor); + } + + [Theory] + [InlineData(20)] + [InlineData(int.MaxValue)] + public void ToPage_SetValue_ReturnsExpected(int toPage) + { + var printerSettings = new PrinterSettings() + { + ToPage = toPage + }; + + Assert.Equal(toPage, printerSettings.ToPage); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void ToPage_Invalid_ThrowsArgumentException(int toPage) + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.ToPage = toPage); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void Clone_Success() + { + var printerSettings = new PrinterSettings(); + PrinterSettings clone = Assert.IsAssignableFrom(printerSettings.Clone()); + Assert.False(ReferenceEquals(clone, printerSettings)); + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void CreateMeasurementGraphics_Default_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + using (Graphics graphic = printerSettings.CreateMeasurementGraphics()) + { + Assert.NotNull(graphic); + Assert.Equal((double)printerSettings.DefaultPageSettings.Bounds.X, graphic.VisibleClipBounds.X, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.Bounds.Y, graphic.VisibleClipBounds.Y, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Height, graphic.VisibleClipBounds.Height, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Width, graphic.VisibleClipBounds.Width, 0); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void CreateMeasurementGraphics_Bool_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + using (Graphics graphic = printerSettings.CreateMeasurementGraphics(true)) + { + Assert.NotNull(graphic); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Height, graphic.VisibleClipBounds.Height, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Width, graphic.VisibleClipBounds.Width, 0); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void CreateMeasurementGraphics_PageSettings_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + var pageSettings = new PageSettings(); + using (Graphics graphic = printerSettings.CreateMeasurementGraphics(pageSettings)) + { + Assert.NotNull(graphic); + Assert.Equal((double)printerSettings.DefaultPageSettings.Bounds.X, graphic.VisibleClipBounds.X, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.Bounds.Y, graphic.VisibleClipBounds.Y, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Height, graphic.VisibleClipBounds.Height, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Width, graphic.VisibleClipBounds.Width, 0); + } + } + + [ConditionalFact(Helpers.AnyInstalledPrinters, Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void CreateMeasurementGraphics_PageSettingsBool_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + var pageSettings = new PageSettings(); + using (Graphics graphic = printerSettings.CreateMeasurementGraphics(pageSettings, true)) + { + Assert.NotNull(graphic); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Height, graphic.VisibleClipBounds.Height, 0); + Assert.Equal((double)printerSettings.DefaultPageSettings.PrintableArea.Width, graphic.VisibleClipBounds.Width, 0); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported, Helpers.WindowsRS3OrEarlier)] // RS4 failures: https://github.com/dotnet/runtime/issues/26247 + public void CreateMeasurementGraphics_Null_ThrowsNullReferenceException() + { + var printerSettings = new PrinterSettings(); + Assert.Throws(() => printerSettings.CreateMeasurementGraphics(null)); + Assert.Throws(() => printerSettings.CreateMeasurementGraphics(null, true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdevmode_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + IntPtr handle = IntPtr.Zero; + + handle = printerSettings.GetHdevmode(); + Assert.NotEqual(IntPtr.Zero, handle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdevmode_PageSettings_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + var pageSettings = new PageSettings(); + IntPtr handle = IntPtr.Zero; + + handle = printerSettings.GetHdevmode(pageSettings); + Assert.NotEqual(IntPtr.Zero, handle); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdevmode_Null_ThrowsNullReferenceException() + { + var printerSettings = new PrinterSettings(); + Assert.Throws(() => printerSettings.GetHdevmode(null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdevnames_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + IntPtr handle = IntPtr.Zero; + + handle = printerSettings.GetHdevnames(); + Assert.NotEqual(IntPtr.Zero, handle); + } + + [ConditionalFact(typeof(PrinterSettingsTests), nameof(CanTestSetHdevmode_IntPtr_Success))] + public void SetHdevmode_IntPtr_Success() + { + string printerName = GetNameOfTestPrinterSuitableForDevModeTesting(); + var printerSettings = new PrinterSettings() { PrinterName = printerName, Copies = 3 }; + var newPrinterSettings = new PrinterSettings() { PrinterName = printerName, Copies = 6 }; + + IntPtr handle = printerSettings.GetHdevmode(); + newPrinterSettings.SetHdevmode(handle); + Assert.Equal(printerSettings.Copies, newPrinterSettings.Copies); + Assert.Equal(printerSettings.Collate, newPrinterSettings.Collate); + Assert.Equal(printerSettings.Duplex, newPrinterSettings.Duplex); + } + + public static bool CanTestSetHdevmode_IntPtr_Success => Helpers.GetIsDrawingSupported() && GetNameOfTestPrinterSuitableForDevModeTesting() != null; + + private static string GetNameOfTestPrinterSuitableForDevModeTesting() + { + foreach (string candidate in s_TestPrinterNames) + { + PrinterSettings printerSettings = new PrinterSettings() { PrinterName = candidate }; + if (printerSettings.IsValid) + return candidate; + } + return null; + } + + private static readonly string[] s_TestPrinterNames = + { + // Our method of testing some apis requires a printer that supports multi-copy printing, collating, color and duplex settings. Not all printers + // support these so rather than trust the machine running the test to have configured such a printer as the default, use the name of + // a known compliant printer that ships with Windows 10. + "Microsoft Print to PDF", + "Microsoft XPS Document Writer", // Backup for older Windows + }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHdevmode_Zero_ThrowsArgumentException() + { + var printerSettings = new PrinterSettings(); + AssertExtensions.Throws(null, () => printerSettings.SetHdevmode(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetHdevnames_IntPtr_Success() + { + var printerSettings = new PrinterSettings(); + var newPrinterSettings = new PrinterSettings(); + IntPtr handle = printerSettings.GetHdevnames(); + newPrinterSettings.SetHdevnames(handle); + Assert.Equal(newPrinterSettings.PrinterName, printerSettings.PrinterName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToString_ReturnsExpected() + { + var printerSettings = new PrinterSettings(); + var expected = "[PrinterSettings " + + printerSettings.PrinterName + + " Copies=" + printerSettings.Copies.ToString(CultureInfo.InvariantCulture) + + " Collate=" + printerSettings.Collate.ToString(CultureInfo.InvariantCulture) + + " Duplex=" + printerSettings.Duplex.ToString() + + " FromPage=" + printerSettings.FromPage.ToString(CultureInfo.InvariantCulture) + + " LandscapeAngle=" + printerSettings.LandscapeAngle.ToString(CultureInfo.InvariantCulture) + + " MaximumCopies=" + printerSettings.MaximumCopies.ToString(CultureInfo.InvariantCulture) + + " OutputPort=" + printerSettings.PrintFileName.ToString(CultureInfo.InvariantCulture) + + " ToPage=" + printerSettings.ToPage.ToString(CultureInfo.InvariantCulture) + + "]"; + + Assert.Equal(expected, printerSettings.ToString()); + } + } +} diff --git a/src/System.Drawing.Common/tests/Printing/PrinterUnitConvertTests.cs b/src/System.Drawing.Common/tests/Printing/PrinterUnitConvertTests.cs new file mode 100644 index 00000000000..479dc5e06c1 --- /dev/null +++ b/src/System.Drawing.Common/tests/Printing/PrinterUnitConvertTests.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// Author: +// +// Jordi Mas i Hernandez (jordi@ximian.com) +// + +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class PrinterUnitConvertTests + { + [Theory] + [InlineData(PrinterUnit.Display, PrinterUnit.Display, 100)] + [InlineData(PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter, 2540)] + [InlineData(PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter, 254)] + [InlineData(PrinterUnit.Display, PrinterUnit.ThousandthsOfAnInch, 1000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.Display, 4)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 100)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 10)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 39)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display, 39)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 1000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 100)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 394)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.Display, 10)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.HundredthsOfAMillimeter, 254)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.TenthsOfAMillimeter, 25)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.ThousandthsOfAnInch, 100)] + public void Convert_Int_ReturnsExpected(PrinterUnit fromUnit, PrinterUnit toUnit, int expectedResult) + { + var converted = PrinterUnitConvert.Convert(100, fromUnit, toUnit); + Assert.Equal(expectedResult, converted); + } + + [Theory] + [InlineData(PrinterUnit.Display, PrinterUnit.Display, 100, 1000)] + [InlineData(PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter, 2540, 25400)] + [InlineData(PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.Display, PrinterUnit.ThousandthsOfAnInch, 1000, 10000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.Display, 4, 39)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 10, 100)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 1000, 10000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 394, 3937)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.Display, 10, 100)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.HundredthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.TenthsOfAMillimeter, 25, 254)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.ThousandthsOfAnInch, 100, 1000)] + public void Convert_Point_ReturnsExpected(PrinterUnit fromUnit, PrinterUnit toUnit, int expectedX, int expectedY) + { + var converted = PrinterUnitConvert.Convert(new Point(100, 1000), fromUnit, toUnit); + Assert.Equal(new Point(expectedX, expectedY), converted); + } + + [Theory] + [InlineData(PrinterUnit.Display, PrinterUnit.Display, 100, 1000)] + [InlineData(PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter, 2540, 25400)] + [InlineData(PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.Display, PrinterUnit.ThousandthsOfAnInch, 1000, 10000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.Display, 4, 39)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 10, 100)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 1000, 10000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 394, 3937)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.Display, 10, 100)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.HundredthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.TenthsOfAMillimeter, 25, 254)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.ThousandthsOfAnInch, 100, 1000)] + public void Convert_Rectangle_ReturnsExpected(PrinterUnit fromUnit, PrinterUnit toUnit, int expectedLeftValue, int expectedRightValue) + { + var converted = PrinterUnitConvert.Convert(new Rectangle(100, 1000, 100, 1000), fromUnit, toUnit); + Assert.Equal(new Rectangle(expectedLeftValue, expectedRightValue, expectedLeftValue, expectedRightValue), converted); + } + + [Theory] + [InlineData(PrinterUnit.Display, PrinterUnit.Display, 100, 1000)] + [InlineData(PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter, 2540, 25400)] + [InlineData(PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.Display, PrinterUnit.ThousandthsOfAnInch, 1000, 10000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.Display, 4, 39)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 10, 100)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 1000, 10000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 100, 1000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 394, 3937)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.Display, 10, 100)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.HundredthsOfAMillimeter, 254, 2540)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.TenthsOfAMillimeter, 25, 254)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.ThousandthsOfAnInch, 100, 1000)] + public void Convert_Size_ReturnsExpected(PrinterUnit fromUnit, PrinterUnit toUnit, int expectedX, int expectedY) + { + var converted = PrinterUnitConvert.Convert(new Size(100, 1000), fromUnit, toUnit); + Assert.Equal(new Size(expectedX, expectedY), converted); + } + + [Theory] + [InlineData(PrinterUnit.Display, PrinterUnit.Display, 100, 1000, 100, 1000)] + [InlineData(PrinterUnit.Display, PrinterUnit.HundredthsOfAMillimeter, 2540, 25400, 2540, 25400)] + [InlineData(PrinterUnit.Display, PrinterUnit.TenthsOfAMillimeter, 254, 2540, 254, 2540)] + [InlineData(PrinterUnit.Display, PrinterUnit.ThousandthsOfAnInch, 1000, 10000, 1000, 10000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.Display, 4, 39, 4, 39)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 100, 1000, 100, 1000)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 10, 100, 10, 100)] + [InlineData(PrinterUnit.HundredthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 39, 394, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.Display, 39, 394, 39, 394)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.HundredthsOfAMillimeter, 1000, 10000, 1000, 10000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.TenthsOfAMillimeter, 100, 1000, 100, 1000)] + [InlineData(PrinterUnit.TenthsOfAMillimeter, PrinterUnit.ThousandthsOfAnInch, 394, 3937, 394, 3937)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.Display, 10, 100, 10, 100)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.HundredthsOfAMillimeter, 254, 2540, 254, 2540)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.TenthsOfAMillimeter, 25, 254, 25, 254)] + [InlineData(PrinterUnit.ThousandthsOfAnInch, PrinterUnit.ThousandthsOfAnInch, 100, 1000, 100, 1000)] + public void Convert_Margins_ReturnsExpected(PrinterUnit fromUnit, PrinterUnit toUnit, int expectedLeft, int expectedRight, int expectedTop, int expectedBottom) + { + var converted = PrinterUnitConvert.Convert(new Margins(100, 1000, 100, 1000), fromUnit, toUnit); + Assert.Equal(new Margins(expectedLeft, expectedRight, expectedTop, expectedBottom), converted); + } + } +} diff --git a/src/System.Drawing.Common/tests/RegionTests.cs b/src/System.Drawing.Common/tests/RegionTests.cs new file mode 100644 index 00000000000..116188a4b53 --- /dev/null +++ b/src/System.Drawing.Common/tests/RegionTests.cs @@ -0,0 +1,2384 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Copyright (C) 2004-2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Tests +{ + public class RegionTests + { + private static readonly Graphics s_graphic = Graphics.FromImage(new Bitmap(1, 1)); + + private static Region CreateDisposedRegion() + { + var region = new Region(); + region.Dispose(); + return region; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + using (var region = new Region()) + { + Assert.False(region.IsEmpty(s_graphic)); + Assert.True(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF(-4194304, -4194304, 8388608, 8388608), region.GetBounds(s_graphic)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, -2, -3, -4, true)] + [InlineData(0, 0, 0, 0, true)] + [InlineData(1, 2, 3, 4, false)] + public void Ctor_Rectangle(int x, int y, int width, int height, bool isEmpty) + { + var rectangle = new Rectangle(x, y, width, height); + + using (var region = new Region(rectangle)) + { + Assert.Equal(isEmpty, region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF(x, y, width, height), region.GetBounds(s_graphic)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(1, 2, 3, float.NegativeInfinity, true)] + [InlineData(-1, -2, -3, -4, true)] + [InlineData(0, 0, 0, 0, true)] + [InlineData(1, 2, 3, 4, false)] + [InlineData(float.MaxValue, float.MaxValue, float.MaxValue, float.MaxValue, true)] + public void Ctor_RectangleF(float x, float y, float width, float height, bool isEmpty) + { + var rectangle = new RectangleF(x, y, width, height); + + using (var region = new Region(rectangle)) + { + Assert.Equal(isEmpty, region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(rectangle, region.GetBounds(s_graphic)); + } + } + + public static IEnumerable Region_TestData() + { + yield return new object[] { new Region() }; + yield return new object[] { new Region(new Rectangle(0, 0, 0, 0)) }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Region_TestData))] + public void Ctor_RegionData(Region region) + { + using (region) + { + using (var otherRegion = new Region(region.GetRegionData())) + using (var matrix = new Matrix()) + { + Assert.Equal(region.GetBounds(s_graphic), otherRegion.GetBounds(s_graphic)); + Assert.Equal(region.GetRegionScans(matrix), otherRegion.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_RegionDataOfRegionWithPath_Success() + { + using (var graphicsPath = new GraphicsPath()) + { + graphicsPath.AddRectangle(new Rectangle(1, 2, 3, 4)); + + using (var region = new Region(graphicsPath)) + { + Ctor_RegionData(region); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_RegionDataOfRegionWithRegionData_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var other = new Region(region.GetRegionData())) + { + Ctor_RegionData(other); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullRegionData_ThrowsArgumentNullException() + { + AssertExtensions.Throws("rgnData", () => new Region((RegionData)null)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(1)] + [InlineData(7)] + [InlineData(256)] + public void Ctor_InvalidRegionData_ThrowsExternalException(int dataLength) + { + using (var region = new Region()) + { + RegionData regionData = region.GetRegionData(); + regionData.Data = new byte[dataLength]; + Assert.Throws(() => new Region(regionData)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_EmptyGraphicsPath_ThrowsExternalException() + { + using (var graphicsPath = new GraphicsPath()) + using (var region = new Region(graphicsPath)) + { + RegionData regionData = region.GetRegionData(); + Assert.Throws(() => new Region(regionData)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullDataInRegionData_ThrowsNullReferenceException() + { + using (var region = new Region()) + { + RegionData regionData = region.GetRegionData(); + regionData.Data = null; + Assert.Throws(() => new Region(regionData)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_GraphicsPath() + { + using (var graphicsPath = new GraphicsPath()) + { + graphicsPath.AddRectangle(new Rectangle(1, 2, 3, 4)); + graphicsPath.AddRectangle(new Rectangle(4, 5, 6, 7)); + + using (var region = new Region(graphicsPath)) + using (var matrix = new Matrix()) + { + Assert.Equal(new RectangleF[] + { + new RectangleF(1, 2, 3, 3), + new RectangleF(1, 5, 9, 1), + new RectangleF(4, 6, 6, 6) + }, region.GetRegionScans(matrix)); + Assert.Equal(new RectangleF(1, 2, 9, 10), region.GetBounds(s_graphic)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_EmptyGraphicsPath() + { + using (var graphicsPath = new GraphicsPath()) + using (var region = new Region(graphicsPath)) + using (var matrix = new Matrix()) + { + Assert.True(region.IsEmpty(s_graphic)); + Assert.Empty(region.GetRegionScans(matrix)); + } + } + + public static IEnumerable Ctor_InfiniteGraphicsPath_TestData() + { + var path1 = new GraphicsPath(); + path1.AddRectangle(new Rectangle(-4194304, -4194304, 8388608, 8388608)); + yield return new object[] { path1, true }; + + var path2 = new GraphicsPath(); + path2.AddRectangle(new Rectangle(-4194304, -4194304, 8388608, 8388608)); + path2.AddRectangle(Rectangle.Empty); + yield return new object[] { path2, true }; + + var path3 = new GraphicsPath(); + path3.AddRectangle(new Rectangle(-4194304, -4194304, 8388608, 8388608)); + path3.AddRectangle(new Rectangle(1, 2, 3, 4)); + yield return new object[] { path3, false }; + + var path4 = new GraphicsPath(); + path4.AddCurve(new Point[] { new Point(-4194304, -4194304), new Point(4194304, 4194304) }); + yield return new object[] { path4, false }; + + var path5 = new GraphicsPath(); + path5.AddPolygon(new Point[] { new Point(-4194304, -4194304), new Point(-4194304, 4194304), new Point(4194304, 4194304), new Point(4194304, -4194304) }); + yield return new object[] { path5, true }; + + var path6 = new GraphicsPath(); + path6.AddPolygon(new Point[] { new Point(-4194304, -4194304), new Point(-4194304, 4194304), new Point(4194304, 4194304), new Point(4194304, -4194304), new Point(-4194304, -4194304) }); + yield return new object[] { path6, true }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_InfiniteGraphicsPath_TestData))] + public void Ctor_InfiniteGraphicsPath_IsInfinite(GraphicsPath path, bool isInfinite) + { + using (path) + using (var region = new Region(path)) + { + Assert.Equal(isInfinite, region.IsInfinite(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_GraphicsPathTooLarge_SetsToEmpty() + { + using (var path = new GraphicsPath()) + { + path.AddCurve(new Point[] { new Point(-4194304, -4194304), new Point(4194304, 4194304) }); + + using (var region = new Region(path)) + using (var matrix = new Matrix()) + { + Assert.Empty(region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullGraphicsPath_ThrowsArgumentNullException() + { + AssertExtensions.Throws("path", () => new Region((GraphicsPath)null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedGraphicsPath_ThrowsArgumentException() + { + var path = new GraphicsPath(); + path.Dispose(); + + AssertExtensions.Throws(null, () => new Region(path)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Region_TestData))] + public void Clone(Region region) + { + using (region) + using (Region clone = Assert.IsType(region.Clone())) + using (var matrix = new Matrix()) + { + Assert.NotSame(region, clone); + + Assert.Equal(region.GetBounds(s_graphic), clone.GetBounds(s_graphic)); + Assert.Equal(region.GetRegionScans(matrix), clone.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().Clone()); + } + + public static IEnumerable Complement_TestData() + { + yield return new object[] + { + new Region(new RectangleF(10, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] { new RectangleF(110, 60, 30, 20) } + }; + + yield return new object[] + { + new Region(new RectangleF(70, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] { new RectangleF(40, 60, 30, 20) } + }; + + yield return new object[] + { + new Region(new RectangleF(40, 100, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] { new RectangleF(70, 80, 50, 20) } + }; + + yield return new object[] + { + new Region(new RectangleF(40, 10, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] { new RectangleF(70, 110, 50, 10) } + }; + + yield return new object[] + { + new Region(new RectangleF(30, 30, 80, 80)), + new RectangleF[] + { + new RectangleF(45, 45, 200, 200), + new RectangleF(160, 260, 10, 10), + new RectangleF(170, 260, 10, 10), + }, + new RectangleF[] { new RectangleF(170, 260, 10, 10) } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { new RectangleF(1, 2, 3, 4) }, + new RectangleF[0] + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Complement_TestData))] + public void Complement_Region_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var other = new Region(rect)) + { + region.Complement(other); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_UnionRegion_Success() + { + using (var region = new Region(new Rectangle(20, 20, 20, 20))) + using (var other = new Region(new Rectangle(20, 80, 20, 10))) + using (var matrix = new Matrix()) + { + other.Union(new Rectangle(60, 60, 30, 10)); + + region.Complement(other); + Assert.Equal(new RectangleF[] + { + new RectangleF(60, 60, 30, 10), + new RectangleF(20, 80, 20, 10) + }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_InfiniteAndWithIntersectRegion_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Intersect(new Rectangle(5, 5, -10, -10)); + region.Complement(new Rectangle(-5, -5, 12, 12)); + + Assert.False(region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] + { + new RectangleF(5, -5, 2, 10), + new RectangleF(-5, 5, 12, 2) + }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_InfiniteRegion_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var matrix = new Matrix()) + using (var other = new Region()) + { + region.Complement(other); + + Assert.Equal(new RectangleF[] + { + new RectangleF(-4194304, -4194304, 8388608, 4194306), + new RectangleF(-4194304, 2, 4194305, 4), + new RectangleF(4, 2, 4194300, 4), + new RectangleF(-4194304, 6, 8388608, 4194298) + }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Complement((Region)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_DisposedRegion_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Region().Complement(CreateDisposedRegion())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_SameRegion_ThrowsInvalidOperationException() + { + using (var region = new Region()) + { + Assert.Throws(() => region.Complement(region)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Complement_TestData))] + public void Complement_Rectangle_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Complement(new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Complement_TestData))] + public void Complement_RectangleF_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Complement(rect); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Complement_TestData))] + public void Complement_GraphicsPath_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var path = new GraphicsPath()) + { + path.AddRectangle(rect); + region.Complement(path); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_GraphicsPathWithMultipleRectangles_Success() + { + var rect1 = new Rectangle(20, 30, 60, 80); + var rect2 = new Rectangle(50, 40, 60, 80); + + using (Graphics graphics = Graphics.FromImage(new Bitmap(600, 800))) + using (var region1 = new Region(rect1)) + using (var region2 = new Region(rect2)) + using (var matrix = new Matrix()) + { + graphics.DrawRectangle(Pens.Green, rect1); + graphics.DrawRectangle(Pens.Red, rect2); + + region1.Complement(region2); + graphics.FillRegion(Brushes.Blue, region1); + graphics.DrawRectangles(Pens.Yellow, region1.GetRegionScans(matrix)); + + Assert.Equal(new RectangleF[] + { + new RectangleF(80, 40, 30, 70), + new RectangleF(50, 110, 60, 10) + }, region1.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_EmptyPathWithInfiniteRegion_MakesEmpty() + { + using (var region = new Region()) + using (var graphicsPath = new GraphicsPath()) + { + region.Complement(graphicsPath); + Assert.True(region.IsEmpty(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_NullGraphicsPath_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("path", () => region.Complement((GraphicsPath)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Complement_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + using (var graphicPath = new GraphicsPath()) + using (var other = new Region()) + { + AssertExtensions.Throws(null, () => disposedRegion.Complement(graphicPath)); + AssertExtensions.Throws(null, () => disposedRegion.Complement(new Rectangle())); + AssertExtensions.Throws(null, () => disposedRegion.Complement(new RectangleF())); + AssertExtensions.Throws(null, () => disposedRegion.Complement(disposedRegion)); + } + } + + public static IEnumerable Equals_TestData() + { + static Region Empty() + { + var emptyRegion = new Region(); + emptyRegion.MakeEmpty(); + return emptyRegion; + } + + var createdRegion = new Region(); + yield return new object[] { createdRegion, createdRegion, true }; + yield return new object[] { new Region(), new Region(), true }; + yield return new object[] { new Region(), Empty(), false }; + yield return new object[] { new Region(), new Region(new Rectangle(1, 2, 3, 4)), false }; + + yield return new object[] { Empty(), Empty(), true }; + yield return new object[] { Empty(), new Region(new Rectangle(0, 0, 0, 0)), true }; + yield return new object[] { Empty(), new Region(new Rectangle(1, 2, 3, 3)), false }; + + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new Rectangle(1, 2, 3, 4)), true }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new RectangleF(1, 2, 3, 4)), true }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new Rectangle(2, 2, 3, 4)), false }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new Rectangle(1, 3, 3, 4)), false }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new Rectangle(1, 2, 4, 4)), false }; + yield return new object[] { new Region(new Rectangle(1, 2, 3, 4)), new Region(new Rectangle(1, 2, 3, 5)), false }; + + var graphics1 = new GraphicsPath(); + graphics1.AddRectangle(new Rectangle(1, 2, 3, 4)); + + var graphics2 = new GraphicsPath(); + graphics2.AddRectangle(new Rectangle(1, 2, 3, 4)); + + var graphics3 = new GraphicsPath(); + graphics3.AddRectangle(new Rectangle(2, 2, 3, 4)); + + var graphics4 = new GraphicsPath(); + graphics4.AddRectangle(new Rectangle(1, 3, 3, 4)); + + var graphics5 = new GraphicsPath(); + graphics5.AddRectangle(new Rectangle(1, 2, 4, 4)); + + var graphics6 = new GraphicsPath(); + graphics6.AddRectangle(new Rectangle(1, 2, 3, 5)); + + yield return new object[] { new Region(graphics1), new Region(graphics1), true }; + yield return new object[] { new Region(graphics1), new Region(graphics2), true }; + yield return new object[] { new Region(graphics1), new Region(graphics3), false }; + yield return new object[] { new Region(graphics1), new Region(graphics4), false }; + yield return new object[] { new Region(graphics1), new Region(graphics5), false }; + yield return new object[] { new Region(graphics1), new Region(graphics6), false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Equals_TestData))] + public void Equals_Valid_ReturnsExpected(Region region, Region other, bool expected) + { + using (region) + using (other) + { + Assert.Equal(expected, region.Equals(other, s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Equals(null, s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_NullGraphics_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("g", () => region.Equals(region, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_DisposedGraphics_ThrowsArgumentException() + { + using (var region = new Region()) + using (var other = new Region()) + using (var image = new Bitmap(10, 10)) + { + var graphics = Graphics.FromImage(image); + graphics.Dispose(); + AssertExtensions.Throws(null, () => region.Equals(region, graphics)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Equals_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + AssertExtensions.Throws(null, () => disposedRegion.Equals(new Region(), s_graphic)); + AssertExtensions.Throws(null, () => new Region().Equals(disposedRegion, s_graphic)); + } + + public static IEnumerable Exclude_TestData() + { + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { new RectangleF(520, 40, 60, 80) }, + new RectangleF[] + { + new RectangleF(-4194304, -4194304, 8388608, 4194344), + new RectangleF(-4194304, 40, 4194824, 80), + new RectangleF(580, 40, 4193724, 80), + new RectangleF(-4194304, 120, 8388608, 4194184) + } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new Rectangle(-4194304, -4194304, 8388608, 8388608) } + }; + + // Intersecting from the right. + yield return new object[] + { + new Region(new Rectangle(10, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] + { + new RectangleF(10, 10, 100, 50), + new RectangleF(10, 60, 30, 20), + new RectangleF(10, 80, 100, 30) + } + }; + + // Intersecting from the left. + yield return new object[] + { + new Region(new Rectangle(70, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] + { + new RectangleF(70, 10, 100, 50), + new RectangleF(140, 60, 30, 20), + new RectangleF(70, 80, 100, 30) + } + }; + + // Intersecting from the top. + yield return new object[] + { + new Region(new Rectangle(40, 100, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] + { + new RectangleF(40, 100, 30, 20), + new RectangleF(120, 100, 20, 20), + new RectangleF(40, 120, 100, 80) + } + }; + + // Intersecting from the bottom. + yield return new object[] + { + new Region(new Rectangle(40, 10, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] + { + new RectangleF(40, 10, 100, 70), + new RectangleF(40, 80, 30, 30), + new RectangleF(120, 80, 20, 30) + } + }; + + // Multiple regions. + yield return new object[] + { + new Region(new Rectangle(30, 30, 80, 80)), + new RectangleF[] + { + new RectangleF(45, 45, 200, 200), + new RectangleF(160, 260, 10, 10), + new RectangleF(170, 260, 10, 10) + }, + new RectangleF[] + { + new RectangleF(30, 30, 80, 15), + new RectangleF(30, 45, 15, 65) + } + }; + + // Intersecting from the top with a larger rect. + yield return new object[] + { + new Region(new Rectangle(50, 100, 100, 100)), + new RectangleF[] { new RectangleF(30, 70, 150, 40) }, + new RectangleF[] { new RectangleF(50, 110, 100, 90) } + }; + + // Intersecting from the right with a larger rect. + yield return new object[] + { + new Region(new Rectangle(70, 60, 100, 70)), + new RectangleF[] { new RectangleF(40, 10, 100, 150) }, + new RectangleF[] { new RectangleF(140, 60, 30, 70) } + }; + + // Intersecting from the left with a larger rect. + yield return new object[] + { + new Region(new Rectangle(70, 60, 100, 70)), + new RectangleF[] { new RectangleF(100, 10, 100, 150) }, + new RectangleF[] { new RectangleF(70, 60, 30, 70) } + }; + + // Intersecting from the bottom with a larger rect. + yield return new object[] + { + new Region(new Rectangle(20, 20, 100, 100)), + new RectangleF[] { new RectangleF(10, 80, 140, 150) }, + new RectangleF[] { new RectangleF(20, 20, 100, 60) } + }; + + yield return new object[] + { + new Region(new Rectangle(130, 30, 60, 80)), + new RectangleF[] { new RectangleF(170, 40, 60, 80) }, + new RectangleF[] + { + new RectangleF(130, 30, 60, 10), + new RectangleF(130, 40, 40, 70) + } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Exclude_TestData))] + public void Exclude_Region_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var other = new Region(rect)) + { + region.Exclude(other); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_UnionRegion_Success() + { + using (var region = new Region(new RectangleF(20, 20, 20, 20))) + using (var union = new Region(new RectangleF(20, 80, 20, 10))) + using (var matrix = new Matrix()) + { + union.Union(new RectangleF(60, 60, 30, 10)); + region.Exclude(union); + Assert.Equal(new RectangleF[] { new RectangleF(20, 20, 20, 20) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_InfiniteRegion_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var other = new Region()) + using (var matrix = new Matrix()) + { + region.Exclude(other); + Assert.Equal(new RectangleF[0], region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Exclude((Region)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_DisposedRegion_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Region().Exclude(CreateDisposedRegion())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_SameRegion_ThrowsInvalidOperationException() + { + using (var region = new Region()) + { + Assert.Throws(() => region.Exclude(region)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Exclude_TestData))] + public void Exclude_Rectangle_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Exclude(new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Exclude_TestData))] + public void Exclude_RectangleF_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Exclude(rect); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Exclude_TestData))] + public void Exclude_GraphicsPath_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var path = new GraphicsPath()) + { + path.AddRectangle(rect); + region.Exclude(path); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_EmptyPathWithInfiniteRegion_MakesInfinite() + { + using (var region = new Region()) + using (var graphicsPath = new GraphicsPath()) + { + region.Exclude(graphicsPath); + Assert.True(region.IsInfinite(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_NullGraphicsPath_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("path", () => region.Exclude((GraphicsPath)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Exclude_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + using (var graphicsPath = new GraphicsPath()) + using (var other = new Region()) + { + AssertExtensions.Throws(null, () => disposedRegion.Exclude(graphicsPath)); + AssertExtensions.Throws(null, () => disposedRegion.Exclude(new Rectangle())); + AssertExtensions.Throws(null, () => disposedRegion.Exclude(new RectangleF())); + AssertExtensions.Throws(null, () => disposedRegion.Exclude(other)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHrgn_ValidHrgn_ReturnsExpected() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + { + IntPtr handle1 = region.GetHrgn(s_graphic); + IntPtr handle2 = region.GetHrgn(s_graphic); + Assert.NotEqual(IntPtr.Zero, handle1); + Assert.NotEqual(handle1, handle2); + + Region newRegion = Region.FromHrgn(handle1); + IntPtr handle3 = newRegion.GetHrgn(s_graphic); + Assert.NotEqual(handle3, handle1); + Assert.Equal(new RectangleF(1, 2, 3, 4), newRegion.GetBounds(s_graphic)); + + region.ReleaseHrgn(handle1); + region.ReleaseHrgn(handle2); + newRegion.ReleaseHrgn(handle3); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FromHrgn_ZeroHrgn_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => Region.FromHrgn(IntPtr.Zero)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHrgn_Infinite_ReturnsZero() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + { + IntPtr handle = region.GetHrgn(s_graphic); + Assert.NotEqual(IntPtr.Zero, handle); + region.ReleaseHrgn(handle); + + region.MakeInfinite(); + Assert.Equal(IntPtr.Zero, region.GetHrgn(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHrgn_Empty_ReturnsNonZero() + { + using (var region = new Region()) + { + Assert.Equal(IntPtr.Zero, region.GetHrgn(s_graphic)); + + region.MakeEmpty(); + IntPtr handle = region.GetHrgn(s_graphic); + Assert.NotEqual(IntPtr.Zero, handle); + region.ReleaseHrgn(handle); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHrgn_NullGraphics_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("g", () => region.GetHrgn(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetHrgn_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().GetHrgn(s_graphic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ReleaseHrgn_ZeroHandle_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("regionHandle", () => region.ReleaseHrgn(IntPtr.Zero)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_NullGraphics_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("g", () => region.GetBounds(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_DisposedGraphics_ThrowsArgumentException() + { + using (var region = new Region()) + using (var image = new Bitmap(10, 10)) + { + var graphics = Graphics.FromImage(image); + graphics.Dispose(); + AssertExtensions.Throws(null, () => region.GetBounds(graphics)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetBounds_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().GetBounds(s_graphic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetRegionData_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().GetRegionData()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetRegionScans_CustomMatrix_TransformsRegionScans() + { + using (var matrix = new Matrix()) + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var emptyMatrix = new Matrix()) + { + matrix.Translate(10, 11); + matrix.Scale(5, 6); + + Assert.Equal(new RectangleF[] { new RectangleF(1, 2, 3, 4) }, region.GetRegionScans(emptyMatrix)); + Assert.Equal(new RectangleF[] { new RectangleF(15, 23, 15, 24) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetRegionScans_NullMatrix_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("matrix", () => region.GetRegionScans(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetRegionScans_Disposed_ThrowsArgumentException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetRegionScans_DisposedMatrix_ThrowsArgumentException() + { + using (var region = new Region()) + { + var matrix = new Matrix(); + matrix.Dispose(); + AssertExtensions.Throws(null, () => region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_SmallerRect_Success() + { + using (var clipRegion = new Region()) + using (var matrix = new Matrix()) + { + Rectangle smaller = new Rectangle(5, 5, -10, -10); + + clipRegion.Intersect(smaller); + Assert.False(clipRegion.IsEmpty(s_graphic)); + Assert.False(clipRegion.IsInfinite(s_graphic)); + + RectangleF[] rects = clipRegion.GetRegionScans(matrix); + Assert.Equal(1, rects.Length); + Assert.Equal(new RectangleF(-5, -5, 10, 10), rects[0]); + } + } + + public static IEnumerable Intersect_TestData() + { + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + yield return new object[] + { + new Region(new Rectangle(0, 0, 0, 0)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { new RectangleF(520, 40, 60, 80) }, + new RectangleF[] { new Rectangle(520, 40, 60, 80) } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(new RectangleF(260, 30, 60, 80)), + new RectangleF[] { new RectangleF(290, 40, 60, 90) }, + new RectangleF[] { new RectangleF(290, 40, 30, 70) } + }; + + yield return new object[] + { + new Region(new RectangleF(20, 330, 40, 50)), + new RectangleF[] + { + new RectangleF(50, 340, 40, 50), + new RectangleF(70, 360, 30, 50), + new RectangleF(80, 400, 30, 10) + }, + new RectangleF[0] + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Intersect_TestData))] + public void Intersect_Region_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var rectangleRegion = new Region(rect)) + { + region.Intersect(rectangleRegion); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_InfiniteRegion_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var matrix = new Matrix()) + using (var infiniteRegion = new Region()) + { + region.Intersect(infiniteRegion); + + Assert.Equal(new RectangleF[] { new Rectangle(1, 2, 3, 4) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Intersect((Region)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_DisposedRegion_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => new Region().Intersect(CreateDisposedRegion())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_SameRegion_ThrowsInvalidOperationException() + { + using (var region = new Region()) + { + Assert.Throws(() => region.Intersect(region)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Intersect_TestData))] + public void Intersect_Rectangle_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Intersect(new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_InfiniteRegionWithSmallerRectangle_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Intersect(new Rectangle(5, 5, -10, -10)); + + Assert.False(region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-5, -5, 10, 10) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Intersect_TestData))] + public void Intersect_RectangleF_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Intersect(rect); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_InfiniteRegionWithSmallerRectangleF_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Intersect(new RectangleF(5, 5, -10, -10)); + + Assert.False(region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-5, -5, 10, 10) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Intersect_TestData))] + public void Intersect_GraphicsPath_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var path = new GraphicsPath()) + { + path.AddRectangle(rect); + region.Intersect(path); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_EmptyPathWithInfiniteRegion_MakesEmpty() + { + using (var region = new Region()) + using (var graphicsPath = new GraphicsPath()) + { + region.Intersect(graphicsPath); + Assert.True(region.IsEmpty(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_NullGraphicsPath_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("path", () => region.Intersect((GraphicsPath)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Intersect_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + using (var graphicsPath = new GraphicsPath()) + using (var other = new Region()) + { + AssertExtensions.Throws(null, () => disposedRegion.Intersect(graphicsPath)); + AssertExtensions.Throws(null, () => disposedRegion.Intersect(new Rectangle())); + AssertExtensions.Throws(null, () => disposedRegion.Intersect(new RectangleF())); + AssertExtensions.Throws(null, () => disposedRegion.Intersect(other)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsEmpty_NullGraphics_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("g", () => region.IsEmpty(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsEmpty_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().IsEmpty(s_graphic)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsInfinite_NullGraphics_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("g", () => region.IsInfinite(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsInfinite_DisposedGraphics_ThrowsArgumentException() + { + using (var region = new Region()) + using (var image = new Bitmap(10, 10)) + { + var graphics = Graphics.FromImage(image); + graphics.Dispose(); + AssertExtensions.Throws(null, () => region.IsInfinite(graphics)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsInfinite_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().IsInfinite(s_graphic)); + } + + public static IEnumerable IsVisible_Rectangle_TestData() + { + var infiniteExclude = new Region(); + infiniteExclude.Exclude(new Rectangle(387, 292, 189, 133)); + infiniteExclude.Exclude(new Rectangle(387, 66, 189, 133)); + + yield return new object[] { infiniteExclude, new Rectangle(66, 292, 189, 133), true }; + yield return new object[] { new Region(), Rectangle.Empty, false }; + + yield return new object[] { new Region(new Rectangle(0, 0, 10, 10)), new Rectangle(0, 0, 0, 1), false }; + yield return new object[] { new Region(new Rectangle(500, 30, 60, 80)), new Rectangle(500, 30, 60, 80), true }; + yield return new object[] { new Region(new Rectangle(500, 30, 60, 80)), new Rectangle(520, 40, 60, 80), true }; + + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(1, 1, 2, 1), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(1, 1, 2, 2), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(1, 1, 10, 10), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(1, 1, 1, 1), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(2, 2, 1, 1), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(0, 0, 1, 1), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Rectangle(3, 3, 1, 1), false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(IsVisible_Rectangle_TestData))] + public void IsVisible_Rectangle_ReturnsExpected(Region region, Rectangle rectangle, bool expected) + { + using (region) + using (var image = new Bitmap(10, 10)) + { + var disposedGraphics = Graphics.FromImage(image); + disposedGraphics.Dispose(); + + Assert.Equal(expected, region.IsVisible(rectangle)); + Assert.Equal(expected, region.IsVisible((RectangleF)rectangle)); + Assert.Equal(expected, region.IsVisible(rectangle, s_graphic)); + Assert.Equal(expected, region.IsVisible(rectangle, disposedGraphics)); + Assert.Equal(expected, region.IsVisible(rectangle, null)); + Assert.Equal(expected, region.IsVisible((RectangleF)rectangle, s_graphic)); + Assert.Equal(expected, region.IsVisible((RectangleF)rectangle, disposedGraphics)); + Assert.Equal(expected, region.IsVisible((RectangleF)rectangle, null)); + + Assert.Equal(expected, region.IsVisible(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)); + Assert.Equal(expected, region.IsVisible((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)); + Assert.Equal(expected, region.IsVisible(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, s_graphic)); + Assert.Equal(expected, region.IsVisible(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, disposedGraphics)); + Assert.Equal(expected, region.IsVisible(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, null)); + Assert.Equal(expected, region.IsVisible((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, s_graphic)); + Assert.Equal(expected, region.IsVisible((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, disposedGraphics)); + Assert.Equal(expected, region.IsVisible((float)rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, null)); + } + } + + public static IEnumerable IsVisible_Point_TestData() + { + var infiniteExclude = new Region(); + infiniteExclude.Exclude(new Rectangle(387, 292, 189, 133)); + infiniteExclude.Exclude(new Rectangle(387, 66, 189, 133)); + + yield return new object[] { infiniteExclude, new Point(66, 292), true }; + yield return new object[] { new Region(), Point.Empty, true }; + + yield return new object[] { new Region(new Rectangle(500, 30, 60, 80)), new Point(500, 29), false }; + yield return new object[] { new Region(new Rectangle(500, 30, 60, 80)), new Point(500, 30), true }; + + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(0, 1), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(1, 0), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(2, 0), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(3, 0), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(1, 1), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(2, 1), true }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(3, 1), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(0, 2), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(2, 2), false }; + yield return new object[] { new Region(new Rectangle(1, 1, 2, 1)), new Point(3, 2), false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(IsVisible_Point_TestData))] + public void IsVisible_Point_ReturnsExpected(Region region, Point point, bool expected) + { + using (region) + using (var image = new Bitmap(10, 10)) + { + var disposedGraphics = Graphics.FromImage(image); + disposedGraphics.Dispose(); + + Assert.Equal(expected, region.IsVisible(point)); + Assert.Equal(expected, region.IsVisible((PointF)point)); + Assert.Equal(expected, region.IsVisible(point, s_graphic)); + Assert.Equal(expected, region.IsVisible(point, disposedGraphics)); + Assert.Equal(expected, region.IsVisible(point, null)); + Assert.Equal(expected, region.IsVisible((PointF)point, s_graphic)); + Assert.Equal(expected, region.IsVisible((PointF)point, disposedGraphics)); + Assert.Equal(expected, region.IsVisible((PointF)point, null)); + + Assert.Equal(expected, region.IsVisible(point.X, point.Y)); + Assert.Equal(expected, region.IsVisible(point.X, point.Y, s_graphic)); + Assert.Equal(expected, region.IsVisible(point.X, point.Y, disposedGraphics)); + Assert.Equal(expected, region.IsVisible(point.X, point.Y, null)); + + Assert.Equal(expected, region.IsVisible(point.X, point.Y, s_graphic)); + Assert.Equal(expected, region.IsVisible(point.X, point.Y, disposedGraphics)); + Assert.Equal(expected, region.IsVisible(point.X, point.Y, null)); + Assert.Equal(expected, region.IsVisible((float)point.X, point.Y, s_graphic)); + Assert.Equal(expected, region.IsVisible((float)point.X, point.Y, disposedGraphics)); + Assert.Equal(expected, region.IsVisible((float)point.X, point.Y, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void IsVisible_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1f, 2f)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new PointF(1, 2))); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new Point(1, 2))); + + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1f, 2f, s_graphic)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new PointF(1, 2), s_graphic)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new Point(1, 2), s_graphic)); + + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1f, 2f, 3f, 4f)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new Rectangle(1, 2, 3, 4))); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new RectangleF(1, 2, 3, 4))); + + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1f, 2f, 3f, 4f, s_graphic)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new Rectangle(1, 2, 3, 4), s_graphic)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(new RectangleF(1, 2, 3, 4), s_graphic)); + + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1, 2, s_graphic)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1, 2, 3, 4)); + AssertExtensions.Throws(null, () => disposedRegion.IsVisible(1, 2, 3, 4, s_graphic)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Region_TestData))] + public void MakeEmpty_NonEmpty_Success(Region region) + { + using (region) + { + region.MakeEmpty(); + Assert.True(region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(RectangleF.Empty, region.GetBounds(s_graphic)); + + using (var matrix = new Matrix()) + { + Assert.Empty(region.GetRegionScans(matrix)); + } + + region.MakeEmpty(); + Assert.True(region.IsEmpty(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeEmpty_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().MakeEmpty()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Region_TestData))] + public void MakeInfinite_NonInfinity_Success(Region region) + { + using (region) + { + region.MakeInfinite(); + Assert.False(region.IsEmpty(s_graphic)); + Assert.True(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF(-4194304, -4194304, 8388608, 8388608), region.GetBounds(s_graphic)); + + region.MakeInfinite(); + Assert.False(region.IsEmpty(s_graphic)); + Assert.True(region.IsInfinite(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MakeInfinite_Disposed_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().MakeInfinite()); + } + + public static IEnumerable Union_TestData() + { + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(520, 30, 60, 80) }, + new RectangleF[] { new RectangleF(500, 30, 80, 80) } + }; + + yield return new object[] + { + new Region(new Rectangle(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(520, 40, 60, 80) }, + new RectangleF[] + { + new RectangleF(500, 30, 60, 10), + new RectangleF(500, 40, 80, 70), + new RectangleF(520, 110, 60, 10), + } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { new RectangleF(520, 40, 60, 80) }, + new RectangleF[] { new Rectangle(-4194304, -4194304, 8388608, 8388608) } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new Rectangle(-4194304, -4194304, 8388608, 8388608) } + }; + + // No intersecting rects. + yield return new object[] + { + new Region(new Rectangle(20, 20, 20, 20)), + new RectangleF[] + { + new RectangleF(20, 80, 20, 10), + new RectangleF(60, 60, 30, 10) + }, + new RectangleF[] + { + new RectangleF(20, 20, 20, 20), + new RectangleF(60, 60, 30, 10), + new RectangleF(20, 80, 20, 10) + } + }; + + yield return new object[] + { + new Region(new Rectangle(20, 180, 40, 50)), + new RectangleF[] + { + new RectangleF(50, 190, 40, 50), + new RectangleF(70, 210, 30, 50) + }, + new RectangleF[] + { + new RectangleF(20, 180, 40, 10), + new RectangleF(20, 190, 70, 20), + new RectangleF(20, 210, 80, 20), + new RectangleF(50, 230, 50, 10), + new RectangleF(70, 240, 30, 20) + } + }; + + yield return new object[] + { + new Region(new Rectangle(20, 330, 40, 50)), + new RectangleF[] + { + new RectangleF(50, 340, 40, 50), + new RectangleF(70, 360, 30, 50), + new RectangleF(80, 400, 30, 10) + }, + new RectangleF[] + { + new RectangleF(20, 330, 40, 10), + new RectangleF(20, 340, 70, 20), + new RectangleF(20, 360, 80, 20), + new RectangleF(50, 380, 50, 10), + new RectangleF(70, 390, 30, 10), + new RectangleF(70, 400, 40, 10) + } + }; + + yield return new object[] + { + new Region(new Rectangle(10, 20, 50, 50)), + new RectangleF[] + { + new RectangleF(100, 100, 60, 60), + new RectangleF(200, 200, 80, 80) + }, + new RectangleF[] + { + new RectangleF(10, 20, 50, 50), + new RectangleF(100, 100, 60, 60), + new RectangleF(200, 200, 80, 80) + } + }; + + // Intersecting from the right. + yield return new object[] + { + new Region(new Rectangle(10, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] + { + new RectangleF(10, 10, 100, 50), + new RectangleF(10, 60, 130, 20), + new RectangleF(10, 80, 100, 30) + } + }; + + // Intersecting from the left. + yield return new object[] + { + new Region(new Rectangle(70, 10, 100, 100)), + new RectangleF[] { new RectangleF(40, 60, 100, 20) }, + new RectangleF[] + { + new RectangleF(70, 10, 100, 50), + new RectangleF(40, 60, 130, 20), + new RectangleF(70, 80, 100, 30) + } + }; + + // Intersecting from the top. + yield return new object[] + { + new Region(new Rectangle(40, 100, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] + { + new RectangleF(70, 80, 50, 20), + new RectangleF(40, 100, 100, 100) + } + }; + + // Intersecting from the bottom. + yield return new object[] + { + new Region(new Rectangle(40, 10, 100, 100)), + new RectangleF[] { new RectangleF(70, 80, 50, 40) }, + new RectangleF[] + { + new RectangleF(40, 10, 100, 100), + new RectangleF(70, 110, 50, 10) + } + }; + + // Multiple regions separated by 0 pixels. + yield return new object[] + { + new Region(new Rectangle(30, 30, 80, 80)), + new RectangleF[] + { + new RectangleF(45, 45, 200, 200), + new RectangleF(160, 260, 10, 10), + new RectangleF(170, 260, 10, 10) + }, + new RectangleF[] + { + new RectangleF(30, 30, 80, 15), + new RectangleF(30, 45, 215, 65), + new RectangleF(45, 110, 200, 135), + new RectangleF(160, 260, 20, 10) + } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Union_TestData))] + public void Union_Region_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var other = new Region(rect)) + { + region.Union(other); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_InfiniteRegion_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var other = new Region()) + using (var matrix = new Matrix()) + { + region.Union(other); + + Assert.Equal(new RectangleF[] { new Rectangle(-4194304, -4194304, 8388608, 8388608) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Union((Region)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_DisposedRegion_ThrowsArgumentException() + { + using (var region = new Region()) + { + AssertExtensions.Throws(null, () => region.Union(CreateDisposedRegion())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_SameRegion_ThrowsInvalidOperationException() + { + using (var region = new Region()) + { + Assert.Throws(() => region.Union(region)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Union_TestData))] + public void Union_Rectangle_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Union(new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Union_TestData))] + public void Union_RectangleF_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Union(rect); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Union_TestData))] + public void Union_GraphicsPath_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var path = new GraphicsPath()) + { + path.AddRectangle(rect); + region.Union(path); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_EmptyPathWithInfiniteRegion_MakesInfinite() + { + using (var region = new Region()) + using (var graphicsPath = new GraphicsPath()) + { + region.Union(graphicsPath); + Assert.True(region.IsInfinite(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_NullGraphicsPath_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("path", () => region.Union((GraphicsPath)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Union_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + using (var graphicsPath = new GraphicsPath()) + using (var other = new Region()) + { + AssertExtensions.Throws(null, () => disposedRegion.Union(graphicsPath)); + AssertExtensions.Throws(null, () => disposedRegion.Union(new Rectangle())); + AssertExtensions.Throws(null, () => disposedRegion.Union(new RectangleF())); + AssertExtensions.Throws(null, () => disposedRegion.Union(disposedRegion)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_EmptyMatrix_Nop() + { + using (var region = new Region(new RectangleF(1, 2, 3, 4))) + using (var matrix = new Matrix()) + { + region.Transform(matrix); + Assert.Equal(new RectangleF[] { new RectangleF(1, 2, 3, 4) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_CustomMatrix_Success() + { + using (var region = new Region(new RectangleF(1, 2, 3, 4))) + using (var matrix = new Matrix()) + using (var emptyMatrix = new Matrix()) + { + matrix.Translate(10, 11); + matrix.Scale(5, 6); + + region.Transform(matrix); + Assert.Equal(new RectangleF[] { new RectangleF(15, 23, 15, 24) }, region.GetRegionScans(emptyMatrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0, 0)] + [InlineData(2, 2, 0)] + [InlineData(0.5, 0.5, 0)] + [InlineData(1, 1, 45)] + public void Transform_Infinity_Nop(float scaleX, float scaleY, int angle) + { + using (var region = new Region()) + using (var matrix = new Matrix()) + using (var emptyMatrix = new Matrix()) + { + matrix.Translate(10, 11); + matrix.Scale(scaleX, scaleY); + matrix.Rotate(angle); + + region.Transform(matrix); + Assert.True(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-4194304, -4194304, 8388608, 8388608) }, region.GetRegionScans(emptyMatrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_InfinityIntersectScale_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + using (var emptyMatrix = new Matrix()) + { + matrix.Scale(2, 0.5f); + + region.Intersect(new Rectangle(-10, -10, 20, 20)); + region.Transform(matrix); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-20, -5, 40, 10) }, region.GetRegionScans(emptyMatrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_InfinityIntersectTransform_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix(2, 0, 0, 0.5f, 10, 10)) + using (var emptyMatrix = new Matrix()) + { + region.Intersect(new Rectangle(-10, -10, 20, 20)); + region.Transform(matrix); + + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-10, 5, 40, 10) }, region.GetRegionScans(emptyMatrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_NullMatrix_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("matrix", () => region.Transform(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_Disposed_ThrowsArgumentException() + { + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => CreateDisposedRegion().Transform(matrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(2, 3)] + [InlineData(-2, -3)] + public void Translate_Int_Success(float dx, float dy) + { + using (var region = new Region(new RectangleF(1, 2, 3, 4))) + using (var matrix = new Matrix()) + { + region.Translate(dx, dy); + Assert.Equal(new RectangleF[] { new RectangleF(1 + dx, 2 + dy, 3, 4) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Translate_IntInfinityIntersect_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Intersect(new Rectangle(-10, -10, 20, 20)); + region.Translate(10, 10); + + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(0, 0, 20, 20) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, 0)] + [InlineData(2, 3)] + public void Translate_Float_Success(int dx, int dy) + { + using (var region = new Region(new RectangleF(1, 2, 3, 4))) + using (var matrix = new Matrix()) + { + region.Translate(dx, dy); + Assert.Equal(new RectangleF[] { new RectangleF(1 + dx, 2 + dy, 3, 4) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Translate_FloatInfinityIntersect_Success() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Intersect(new Rectangle(-10, -10, 20, 20)); + region.Translate(10f, 10f); + + Assert.False(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(0, 0, 20, 20) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Translate_Infinity_Nop() + { + using (var region = new Region()) + using (var matrix = new Matrix()) + { + region.Translate(10, 10); + region.Translate(10f, 10f); + + Assert.True(region.IsInfinite(s_graphic)); + Assert.Equal(new RectangleF[] { new RectangleF(-4194304, -4194304, 8388608, 8388608) }, region.GetRegionScans(matrix)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(float.MaxValue)] + [InlineData(float.MinValue)] + [InlineData(float.NaN)] + [InlineData(float.PositiveInfinity)] + [InlineData(float.NegativeInfinity)] + public void Translate_InvalidFloatValue_EmptiesRegion(float f) + { + using (var region = new Region(new RectangleF(1, 2, 3, 4))) + using (var matrix = new Matrix()) + { + region.Translate(f, 0); + + Assert.True(region.IsEmpty(s_graphic)); + Assert.False(region.IsInfinite(s_graphic)); + Assert.Empty(region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Translate_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + AssertExtensions.Throws(null, () => disposedRegion.Translate(1, 2)); + AssertExtensions.Throws(null, () => disposedRegion.Translate(1f, 2f)); + } + + public static IEnumerable Xor_TestData() + { + yield return new object[] + { + new Region(new RectangleF(500, 30, 60, 80)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[0] + }; + + yield return new object[] + { + new Region(new RectangleF(500, 30, 60, 80)), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + + yield return new object[] + { + new Region(new RectangleF(0, 0, 0, 0)), + new RectangleF[] { new RectangleF(500, 30, 60, 80) }, + new RectangleF[] { new RectangleF(500, 30, 60, 80) } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { new RectangleF(520, 40, 60, 80) }, + new RectangleF[] + { + new RectangleF(-4194304, -4194304, 8388608, 4194344), + new RectangleF(-4194304, 40, 4194824, 80), + new RectangleF(580, 40, 4193724, 80), + new RectangleF(-4194304, 120, 8388608, 4194184) + } + }; + + yield return new object[] + { + new Region(), + new RectangleF[] { RectangleF.Empty }, + new RectangleF[] { new Rectangle(-4194304, -4194304, 8388608, 8388608) } + }; + + yield return new object[] + { + new Region(new RectangleF(380, 30, 60, 80)), + new RectangleF[] { new RectangleF(410, 40, 60, 80) }, + new RectangleF[] + { + new RectangleF(380, 30, 60, 10), + new RectangleF(380, 40, 30, 70), + new RectangleF(440, 40, 30, 70), + new RectangleF(410, 110, 60, 10) + } + }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Xor_TestData))] + public void Xor_Region_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var other = new Region(rect)) + { + region.Xor(other); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_InfiniteRegion_Success() + { + using (var region = new Region(new Rectangle(1, 2, 3, 4))) + using (var other = new Region()) + using (var matrix = new Matrix()) + { + region.Xor(other); + + Assert.Equal(new RectangleF[] + { + new RectangleF(-4194304, -4194304, 8388608, 4194306), + new RectangleF(-4194304, 2, 4194305, 4), + new RectangleF(4, 2, 4194300, 4), + new RectangleF(-4194304, 6, 8388608, 4194298) + }, region.GetRegionScans(matrix)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_NullRegion_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("region", () => region.Xor((Region)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_DisposedRegion_ThrowsArgumentException() + { + using (var region = new Region()) + { + AssertExtensions.Throws(null, () => region.Xor(CreateDisposedRegion())); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_SameRegion_ThrowsInvalidOperationException() + { + using (var region = new Region()) + { + Assert.Throws(() => region.Xor(region)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Xor_TestData))] + public void Xor_Rectangle_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Xor(new Rectangle((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height)); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Xor_TestData))] + public void Xor_RectangleF_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + region.Xor(rect); + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Xor_TestData))] + public void Xor_GraphicsPath_Success(Region region, RectangleF[] rectangles, RectangleF[] expectedScans) + { + using (region) + { + foreach (RectangleF rect in rectangles) + { + using (var path = new GraphicsPath()) + { + path.AddRectangle(rect); + region.Xor(path); + } + } + + using (var matrix = new Matrix()) + { + Assert.Equal(expectedScans, region.GetRegionScans(matrix)); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_EmptyPathWithInfiniteRegion_MakesInfinite() + { + using (var region = new Region()) + using (var graphicsPath = new GraphicsPath()) + { + region.Xor(graphicsPath); + Assert.True(region.IsInfinite(s_graphic)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_NullGraphicsPath_ThrowsArgumentNullException() + { + using (var region = new Region()) + { + AssertExtensions.Throws("path", () => region.Xor((GraphicsPath)null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xor_Disposed_ThrowsArgumentException() + { + Region disposedRegion = CreateDisposedRegion(); + + using (var graphicsPath = new GraphicsPath()) + using (var other = new Region()) + { + AssertExtensions.Throws(null, () => disposedRegion.Xor(graphicsPath)); + AssertExtensions.Throws(null, () => disposedRegion.Xor(new Rectangle())); + AssertExtensions.Throws(null, () => disposedRegion.Xor(new RectangleF())); + AssertExtensions.Throws(null, () => disposedRegion.Xor(other)); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/SolidBrushTests.cs b/src/System.Drawing.Common/tests/SolidBrushTests.cs new file mode 100644 index 00000000000..9b718a99758 --- /dev/null +++ b/src/System.Drawing.Common/tests/SolidBrushTests.cs @@ -0,0 +1,118 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Tests +{ + public class SolidBrushTests + { + public static IEnumerable Colors_TestData() + { + yield return new object[] { new Color(), Color.FromArgb(0) }; + yield return new object[] { Color.PapayaWhip, Color.PapayaWhip }; + } + + [ConditionalTheory(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + [MemberData(nameof(Colors_TestData))] + public void Ctor_Color(Color color, Color expectedColor) + { + var brush = new SolidBrush(color); + Assert.Equal(expectedColor, brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Color_ReturnsClone() + { + var brush = new SolidBrush(Color.PeachPuff); + SolidBrush clone = Assert.IsType(brush.Clone()); + + Assert.NotSame(clone, brush); + Assert.Equal(brush.Color.ToArgb(), clone.Color.ToArgb()); + + // Known colors are not preserved across clones. + Assert.NotEqual(Color.PeachPuff, clone.Color); + + // Modifying the original brush should not modify the clone. + brush.Color = Color.PapayaWhip; + Assert.NotEqual(Color.PapayaWhip, clone.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_ImmutableColor_ReturnsMutableClone() + { + SolidBrush brush = Assert.IsType(Brushes.Bisque); + SolidBrush clone = Assert.IsType(brush.Clone()); + + clone.Color = SystemColors.AppWorkspace; + Assert.Equal(SystemColors.AppWorkspace, clone.Color); + Assert.Equal(Color.Bisque, brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var brush = new SolidBrush(Color.LavenderBlush); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Clone()); + } + + [ConditionalFact(Helpers.IsWindowsOrAtLeastLibgdiplus6)] + public void Color_EmptyAndGetDisposed_ThrowsArgumentException() + { + var brush = new SolidBrush(new Color()); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_NonEmptyAndGetDisposed_ReturnsExpected() + { + var brush = new SolidBrush(Color.Aquamarine); + brush.Dispose(); + + Assert.Equal(Color.Aquamarine, brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_SetValid_GetReturnsExpected() + { + var brush = new SolidBrush(Color.Goldenrod) { Color = Color.GhostWhite }; + Assert.Equal(Color.GhostWhite, brush.Color); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_SetDisposed_ThrowsArgumentException() + { + var brush = new SolidBrush(new Color()); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Color = Color.WhiteSmoke); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Color_SetImmutable_ThrowsArgumentException() + { + SolidBrush brush = Assert.IsType(SystemBrushes.ActiveBorder); + AssertExtensions.Throws(null, () => brush.Color = Color.AntiqueWhite); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Success() + { + var brush = new SolidBrush(Color.Plum); + brush.Dispose(); + brush.Dispose(); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_SetImmutable_ThrowsArgumentException() + { + SolidBrush brush = Assert.IsType(SystemBrushes.ActiveBorder); + AssertExtensions.Throws(null, () => brush.Dispose()); + } + } +} diff --git a/src/System.Drawing.Common/tests/StringFormatTests.cs b/src/System.Drawing.Common/tests/StringFormatTests.cs new file mode 100644 index 00000000000..af72c0952e2 --- /dev/null +++ b/src/System.Drawing.Common/tests/StringFormatTests.cs @@ -0,0 +1,485 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Text; +using Xunit; + +namespace System.Drawing.Tests +{ + public class StringFormatTests + { + private const int RandomLanguageCode = 10; + private const int EnglishLanguageCode = 2057; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + using (var format = new StringFormat()) + { + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal((StringFormatFlags)0, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringFormatFlags.DirectionRightToLeft | StringFormatFlags.DirectionVertical)] + [InlineData((StringFormatFlags)(-1))] + public void Ctor_Options(StringFormatFlags options) + { + using (var format = new StringFormat(options)) + { + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal(options, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringFormatFlags.DirectionRightToLeft | StringFormatFlags.DirectionVertical, RandomLanguageCode)] + [InlineData(StringFormatFlags.NoClip, EnglishLanguageCode)] + [InlineData((StringFormatFlags)(-1), -1)] + public void Ctor_Options_Language(StringFormatFlags options, int language) + { + using (var format = new StringFormat(options, language)) + { + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal(options, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Format() + { + using (var original = new StringFormat(StringFormatFlags.NoClip, EnglishLanguageCode)) + using (var format = new StringFormat(original)) + { + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal(StringFormatFlags.NoClip, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + + // The new format is a clone. + original.FormatFlags = StringFormatFlags.NoFontFallback; + Assert.Equal(StringFormatFlags.NoClip, format.FormatFlags); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullFormat_ThrowsArgumentNullException() + { + AssertExtensions.Throws("format", () => new StringFormat(null)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedFormat_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => new StringFormat(format)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Success() + { + var format = new StringFormat(); + format.Dispose(); + format.Dispose(); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Valid_Success() + { + using (var original = new StringFormat(StringFormatFlags.NoClip, EnglishLanguageCode)) + using (StringFormat format = Assert.IsType(original.Clone())) + { + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal(StringFormatFlags.NoClip, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + + // The new format is a clone. + original.FormatFlags = StringFormatFlags.NoFontFallback; + Assert.Equal(StringFormatFlags.NoClip, format.FormatFlags); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.Clone()); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, StringDigitSubstitute.None, 0)] + [InlineData(EnglishLanguageCode, StringDigitSubstitute.Traditional, EnglishLanguageCode)] + [InlineData(int.MaxValue, StringDigitSubstitute.Traditional + 1, 65535)] + [InlineData(-1, StringDigitSubstitute.User - 1, 65535)] + public void SetDigitSubstitution_Invoke_SetsProperties(int language, StringDigitSubstitute substitute, int expectedLanguage) + { + using (var format = new StringFormat()) + { + format.SetDigitSubstitution(language, substitute); + Assert.Equal(expectedLanguage, format.DigitSubstitutionLanguage); + Assert.Equal(substitute, format.DigitSubstitutionMethod); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetDigitSubstitution_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.SetDigitSubstitution(0, StringDigitSubstitute.None)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0, new float[0])] + [InlineData(10, new float[] { 1, 2.3f, 4, float.PositiveInfinity, float.NaN })] + public void SetTabStops_GetTabStops_ReturnsExpected(float firstTabOffset, float[] tabStops) + { + using (var format = new StringFormat()) + { + format.SetTabStops(firstTabOffset, tabStops); + + Assert.Equal(tabStops, format.GetTabStops(out float actualFirstTabOffset)); + Assert.Equal(firstTabOffset, actualFirstTabOffset); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetTabStops_NullTabStops_ThrowsNullReferenceException() + { + using (var format = new StringFormat()) + { + Assert.Throws(() => format.SetTabStops(0, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetTabStops_NegativeFirstTabOffset_ThrowsArgumentException() + { + using (var format = new StringFormat()) + { + AssertExtensions.Throws(null, () => format.SetTabStops(-1, new float[0])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetTabStops_NegativeInfinityInTabStops_ThrowsNotImplementedException() + { + using (var format = new StringFormat()) + { + Assert.Throws(() => format.SetTabStops(0, new float[] { float.NegativeInfinity })); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetTabStops_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.SetTabStops(0, new float[0])); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetTabStops_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.GetTabStops(out float firstTabOffset)); + } + + public static IEnumerable SetMeasurableCharacterRanges_TestData() + { + yield return new object[] { new CharacterRange[0] }; + yield return new object[] { new CharacterRange[] { new CharacterRange(1, 2) } }; + yield return new object[] { new CharacterRange[] { new CharacterRange(-1, -1) } }; + yield return new object[] { new CharacterRange[32] }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SetMeasurableCharacterRanges_TestData))] + public void SetMeasurableCharacterRanges_Valid_Success(CharacterRange[] ranges) + { + using (var format = new StringFormat()) + { + format.SetMeasurableCharacterRanges(ranges); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetMeasurableCharacterRanges_NullRanges_ThrowsNullReferenceException() + { + using (var format = new StringFormat()) + { + Assert.Throws(() => format.SetMeasurableCharacterRanges(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetMeasurableCharacterRanges_RangesTooLarge_ThrowsOverflowException() + { + using (var format = new StringFormat()) + { + Assert.Throws(() => format.SetMeasurableCharacterRanges(new CharacterRange[33])); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetMeasurableCharacterRanges_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.SetMeasurableCharacterRanges(new CharacterRange[0])); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringAlignment.Center)] + [InlineData(StringAlignment.Far)] + [InlineData(StringAlignment.Near)] + public void Alignment_SetValid_GetReturnsExpected(StringAlignment alignment) + { + using (var format = new StringFormat { Alignment = alignment }) + { + Assert.Equal(alignment, format.Alignment); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringAlignment.Near - 1)] + [InlineData(StringAlignment.Far + 1)] + public void Alignment_SetInvalid_ThrowsInvalidEnumArgumentException(StringAlignment alignment) + { + using (var format = new StringFormat()) + { + Assert.ThrowsAny(() => format.Alignment = alignment); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Alignment_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.Alignment); + AssertExtensions.Throws(null, () => format.Alignment = StringAlignment.Center); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DigitSubstitutionMethod_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.DigitSubstitutionMethod); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DigitSubstitutionLanguage_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.DigitSubstitutionLanguage); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringFormatFlags.DirectionRightToLeft)] + [InlineData((StringFormatFlags)int.MinValue)] + [InlineData((StringFormatFlags)int.MaxValue)] + public void FormatFlags_Set_GetReturnsExpected(StringFormatFlags formatFlags) + { + using (var format = new StringFormat { FormatFlags = formatFlags }) + { + Assert.Equal(formatFlags, format.FormatFlags); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FormatFlags_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.FormatFlags); + AssertExtensions.Throws(null, () => format.FormatFlags = StringFormatFlags.NoClip); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringAlignment.Center)] + [InlineData(StringAlignment.Far)] + [InlineData(StringAlignment.Near)] + public void LineAlignment_SetValid_GetReturnsExpected(StringAlignment alignment) + { + using (var format = new StringFormat { LineAlignment = alignment }) + { + Assert.Equal(alignment, format.LineAlignment); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringAlignment.Near - 1)] + [InlineData(StringAlignment.Far + 1)] + public void LineAlignment_SetInvalid_ThrowsInvalidEnumArgumentException(StringAlignment alignment) + { + using (var format = new StringFormat()) + { + Assert.ThrowsAny(() => format.LineAlignment = alignment); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LineAlignment_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.LineAlignment); + AssertExtensions.Throws(null, () => format.LineAlignment = StringAlignment.Center); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(HotkeyPrefix.Hide)] + [InlineData(HotkeyPrefix.None)] + [InlineData(HotkeyPrefix.Show)] + public void HotKeyPrefix_SetValid_GetReturnsExpected(HotkeyPrefix prefix) + { + using (var format = new StringFormat { HotkeyPrefix = prefix }) + { + Assert.Equal(prefix, format.HotkeyPrefix); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(HotkeyPrefix.None - 1)] + [InlineData(HotkeyPrefix.Hide + 1)] + public void HotKeyPrefix_SetInvalid_ThrowsInvalidEnumArgumentException(HotkeyPrefix prefix) + { + using (var format = new StringFormat()) + { + Assert.ThrowsAny(() => format.HotkeyPrefix = prefix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void HotkeyPrefix_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.HotkeyPrefix); + AssertExtensions.Throws(null, () => format.HotkeyPrefix = HotkeyPrefix.Hide); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringTrimming.Word)] + public void Trimming_SetValid_GetReturnsExpected(StringTrimming trimming) + { + using (var format = new StringFormat { Trimming = trimming }) + { + Assert.Equal(trimming, format.Trimming); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(StringTrimming.None - 1)] + [InlineData(StringTrimming.EllipsisPath + 1)] + public void Trimming_SetInvalid_ThrowsInvalidEnumArgumentException(StringTrimming trimming) + { + using (var format = new StringFormat()) + { + Assert.ThrowsAny(() => format.Trimming = trimming); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Trimming_GetSetWhenDisposed_ThrowsArgumentException() + { + var format = new StringFormat(); + format.Dispose(); + + + AssertExtensions.Throws(null, () => format.Trimming); + AssertExtensions.Throws(null, () => format.Trimming = StringTrimming.Word); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GenericDefault_Get_ReturnsExpected() + { + StringFormat format = StringFormat.GenericDefault; + Assert.NotSame(format, StringFormat.GenericDefault); + + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal((StringFormatFlags)0, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.Character, format.Trimming); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GenericTypographic_Get_ReturnsExpected() + { + StringFormat format = StringFormat.GenericTypographic; + Assert.NotSame(format, StringFormat.GenericTypographic); + + Assert.Equal(StringAlignment.Near, format.Alignment); + Assert.Equal(0, format.DigitSubstitutionLanguage); + Assert.Equal(StringDigitSubstitute.User, format.DigitSubstitutionMethod); + Assert.Equal(StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit | StringFormatFlags.NoClip, format.FormatFlags); + Assert.Equal(HotkeyPrefix.None, format.HotkeyPrefix); + Assert.Equal(StringAlignment.Near, format.LineAlignment); + Assert.Equal(StringTrimming.None, format.Trimming); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToString_Flags_ReturnsExpected() + { + using (var format = new StringFormat(StringFormatFlags.DirectionVertical)) + { + Assert.Equal("[StringFormat, FormatFlags=DirectionVertical]", format.ToString()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ToString_Disposed_ThrowsArgumentException() + { + var format = new StringFormat(StringFormatFlags.DirectionVertical); + format.Dispose(); + + AssertExtensions.Throws(null, () => format.ToString()); + } + } +} diff --git a/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj b/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj new file mode 100644 index 00000000000..2894fc2c4aa --- /dev/null +++ b/src/System.Drawing.Common/tests/System.Drawing.Common.Tests.csproj @@ -0,0 +1,134 @@ + + + + true + annotations + + $(NoWarn);CA1825;CA5351;CA1850 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System.Drawing.Tests.48x48_multiple_entries_4bit.ico + + + System.Drawing.Tests.bitmap_173x183_indexed_8bit.bmp + + + System.Drawing.Tests.empty.file + + + System.Drawing.Tests.invalid.ico + + + System.Drawing.Tests.Icon_toolboxBitmapAttributeTest + + + + + + + + + + + + diff --git a/src/System.Drawing.Common/tests/System/Drawing/FontConverterTests.cs b/src/System.Drawing.Common/tests/System/Drawing/FontConverterTests.cs new file mode 100644 index 00000000000..e2c54362c48 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/FontConverterTests.cs @@ -0,0 +1,217 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Drawing.Text; +using System.Globalization; +using System.Linq; +using Xunit; +using static System.Drawing.FontConverter; + +namespace System.ComponentModel.TypeConverterTests +{ + public class FontNameConverterTest + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertFrom() + { + FontConverter.FontNameConverter converter = new FontConverter.FontNameConverter(); + // returns "Times" under Linux and "Times New Roman" under Windows + if (PlatformDetection.IsWindows) + { + Assert.Equal("Times New Roman", converter.ConvertFrom("Times") as string); + } + else + { + Assert.Equal("Times", converter.ConvertFrom("Times") as string); + } + Assert.True(converter.GetStandardValuesSupported(), "standard values supported"); + Assert.False(converter.GetStandardValuesExclusive(), "standard values exclusive"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ExTestConvertFrom_ThrowsNotSupportedException() + { + FontConverter.FontNameConverter converter = new FontConverter.FontNameConverter(); + Assert.Throws(() => converter.ConvertFrom(null)); + Assert.Throws(() => converter.ConvertFrom(1)); + } + } + + public class FontConverterTest + { + public static char s_Separator = CultureInfo.CurrentCulture.TextInfo.ListSeparator[0]; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TestConvertFormData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Full Framework doesn't support inputs without units")] + public void TestConvertFrom(string input, string expectedName, float expectedSize, GraphicsUnit expectedUnits, FontStyle expectedFontStyle) + { + FontConverter converter = new FontConverter(); + Font font = (Font)converter.ConvertFrom(input); + + // Unix fonts + Assert.Equal(expectedName, font.Name); + Assert.Equal(expectedSize, font.Size); + Assert.Equal(expectedUnits, font.Unit); + Assert.Equal(expectedFontStyle, font.Style); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ArgumentExceptionFontConverterData))] + public void InvalidInputThrowsArgumentException(string input, string paramName, string netfxParamName) + { + FontConverter converter = new FontConverter(); + AssertExtensions.Throws(paramName, netfxParamName, () => converter.ConvertFrom(input)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(InvalidEnumArgumentExceptionFontConverterData))] + public void InvalidInputThrowsInvalidEnumArgumentException(string input, string paramName) + { + FontConverter converter = new FontConverter(); + Assert.Throws(paramName, () => converter.ConvertFrom(input)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void EmptyStringInput() + { + FontConverter converter = new FontConverter(); + Font font = (Font)converter.ConvertFrom(string.Empty); + Assert.Null(font); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotBuiltWithAggressiveTrimming))] + public void GetFontPropsSorted() + { + // The order provided since .NET Framework + string[] expectedPropNames = new[] + { + nameof(Font.Name), + nameof(Font.Size), + nameof(Font.Unit), + nameof(Font.Bold), + nameof(Font.GdiCharSet), + nameof(Font.GdiVerticalFont), + nameof(Font.Italic), + nameof(Font.Strikeout), + nameof(Font.Underline), + }; + + FontConverter converter = new FontConverter(); + Font font = new($"Courier New", 8.25f, FontStyle.Regular, GraphicsUnit.Point); + + PropertyDescriptorCollection props = converter.GetProperties(font); + string[] propNames = new string[props.Count]; + + int index = 0; + foreach (PropertyDescriptor prop in props) + { + propNames[index++] = prop.DisplayName; + } + + Assert.True(propNames.SequenceEqual(expectedPropNames)); + } + + public static TheoryData TestConvertFormData() + { + var data = + new TheoryData() + { + { $"Courier New", "Courier New", 8.25f, GraphicsUnit.Point, FontStyle.Regular }, + { $"Courier New{s_Separator} 11", "Courier New", 11f, GraphicsUnit.Point, FontStyle.Regular }, + { $"Arial{s_Separator} 11px", "Arial", 11f, GraphicsUnit.Pixel, FontStyle.Regular }, + { $"Courier New{s_Separator} 11 px", "Courier New", 11f, GraphicsUnit.Pixel, FontStyle.Regular }, + { $"Courier New{s_Separator} 11 px{s_Separator} style=Regular", "Courier New", 11f, GraphicsUnit.Pixel, FontStyle.Regular }, + { $"Courier New{s_Separator} style=Bold", "Courier New", 8.25f, GraphicsUnit.Point, FontStyle.Bold }, + { $"Courier New{s_Separator} 11 px{s_Separator} style=Bold{s_Separator} Italic", "Courier New", 11f, GraphicsUnit.Pixel, FontStyle.Bold | FontStyle.Italic }, + { $"Courier New{s_Separator} 11 px{s_Separator} style=Regular, Italic", "Courier New", 11f, GraphicsUnit.Pixel, FontStyle.Regular | FontStyle.Italic }, + { $"Courier New{s_Separator} 11 px{s_Separator} style=Bold{s_Separator} Italic{s_Separator} Strikeout", "Courier New", 11f, GraphicsUnit.Pixel, FontStyle.Bold | FontStyle.Italic | FontStyle.Strikeout }, + { $"Arial{s_Separator} 11 px{s_Separator} style=Bold, Italic, Strikeout", "Arial", 11f, GraphicsUnit.Pixel, FontStyle.Bold | FontStyle.Italic | FontStyle.Strikeout }, + { $"11px", "Microsoft Sans Serif", 8.25f, GraphicsUnit.Point, FontStyle.Regular }, + { $"Style=Bold", "Microsoft Sans Serif", 8.25f, GraphicsUnit.Point, FontStyle.Regular }, + { $"arIAL{s_Separator} 10{s_Separator} style=bold", "Arial", 10f, GraphicsUnit.Point, FontStyle.Bold }, + { $"Arial{s_Separator} 10{s_Separator}", "Arial", 10f, GraphicsUnit.Point, FontStyle.Regular }, + { $"Arial{s_Separator}", "Arial", 8.25f, GraphicsUnit.Point, FontStyle.Regular }, + { $"Arial{s_Separator} 10{s_Separator} style=12", "Arial", 10f, GraphicsUnit.Point, FontStyle.Underline | FontStyle.Strikeout }, + { $"Courier New{s_Separator} Style=Bold", "Courier New", 8.25f, GraphicsUnit.Point, FontStyle.Bold }, // FullFramework style keyword is case sensitive. + { $"11px{s_Separator} Style=Bold", "Microsoft Sans Serif", 8.25f, GraphicsUnit.Point, FontStyle.Bold} + }; + + // FullFramework disregards all arguments if the font name is an empty string. + // Empty string is not an installed font on Windows 7, windows 8 and some versions of windows 10. + if (EmptyFontPresent) + { + data.Add($"{s_Separator} 10{s_Separator} style=bold", "", 10f, GraphicsUnit.Point, FontStyle.Bold); + } + else + { + data.Add($"{s_Separator} 10{s_Separator} style=bold", "Microsoft Sans Serif", 10f, GraphicsUnit.Point, FontStyle.Bold); + } + + return data; + } + + private static bool EmptyFontPresent + { + get + { + using (var installedFonts = new InstalledFontCollection()) + { + return installedFonts.Families.Select(t => t.Name).Contains(string.Empty); + } + } + } + + public static TheoryData ArgumentExceptionFontConverterData() => new TheoryData() + { + { $"Courier New{s_Separator} 11 px{s_Separator} type=Bold{s_Separator} Italic", "units", null }, + { $"Courier New{s_Separator} {s_Separator} Style=Bold", "value", null }, + { $"Courier New{s_Separator} 11{s_Separator} Style=", "value", null }, + { $"Courier New{s_Separator} 11{s_Separator} Style=RandomEnum", null, null }, + { $"Arial{s_Separator} 10{s_Separator} style=bold{s_Separator}", "value", null }, + { $"Arial{s_Separator} 10{s_Separator} style=null", null, null }, + { $"Arial{s_Separator} 10{s_Separator} style=abc#", null, null }, + { $"Arial{s_Separator} 10{s_Separator} style=##", null, null }, + { $"Arial{s_Separator} 10display{s_Separator} style=bold", null, null }, + { $"Arial{s_Separator} 10style{s_Separator} style=bold", "units", null }, + }; + + public static TheoryData InvalidEnumArgumentExceptionFontConverterData() => new TheoryData() + { + { $"Arial{s_Separator} 10{s_Separator} style=56", "style" }, + { $"Arial{s_Separator} 10{s_Separator} style=-1", "style" }, + }; + } + + public class FontUnitConverterTest + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetStandardValuesTest() + { + FontUnitConverter converter = new FontUnitConverter(); + var values = converter.GetStandardValues(); + Assert.Equal(6, values.Count); // The six supported values of Graphics unit: World, Pixel, Point, Inch, Document, Millimeter. + + foreach (var item in values) + { + Assert.NotEqual(GraphicsUnit.Display, (GraphicsUnit)item); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("Display", GraphicsUnit.Display)] + [InlineData("Document", GraphicsUnit.Document)] + [InlineData("Inch", GraphicsUnit.Inch)] + [InlineData("Millimeter", GraphicsUnit.Millimeter)] + [InlineData("Pixel", GraphicsUnit.Pixel)] + [InlineData("Point", GraphicsUnit.Point)] + [InlineData("World", GraphicsUnit.World)] + public void CanConvertFrom(string input, GraphicsUnit expected) + { + FontUnitConverter converter = new FontUnitConverter(); + GraphicsUnit value = (GraphicsUnit)converter.ConvertFrom(input); + Assert.Equal(expected, value); + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/IconConverterTests.cs b/src/System.Drawing.Common/tests/System/Drawing/IconConverterTests.cs new file mode 100644 index 00000000000..2a343736964 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/IconConverterTests.cs @@ -0,0 +1,184 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using Xunit; + +namespace System.ComponentModel.TypeConverterTests +{ + // On IoT: "Unable to find an entry point named 'CreateIconFromResourceEx'" + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsIoTCore))] + public class IconConverterTest + { + private readonly Icon _icon; + private readonly IconConverter _icoConv; + private readonly IconConverter _icoConvFrmTD; + private readonly string _iconStr; + private readonly byte[] _iconBytes; + + public IconConverterTest() + { + _icon = new Icon(Path.Combine("bitmaps", "TestIcon.ico")); + _iconStr = _icon.ToString(); + + using (MemoryStream destStream = new MemoryStream()) + { + _icon.Save(destStream); + _iconBytes = destStream.ToArray(); + } + + _icoConv = new IconConverter(); + _icoConvFrmTD = (IconConverter)TypeDescriptor.GetConverter(_icon); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertFrom() + { + Assert.True(_icoConv.CanConvertFrom(typeof(byte[])), "byte[] (no context)"); + Assert.True(_icoConv.CanConvertFrom(null, typeof(byte[])), "byte[]"); + Assert.True(_icoConv.CanConvertFrom(null, _iconBytes.GetType()), "_iconBytes.GetType()"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(string)), "string"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(Rectangle)), "Rectangle"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(Point)), "Point"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(PointF)), "PointF"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(Size)), "Size"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(SizeF)), "SizeF"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(object)), "object"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(int)), "int"); + Assert.False(_icoConv.CanConvertFrom(null, typeof(Metafile)), "Metafile"); + + Assert.True(_icoConvFrmTD.CanConvertFrom(typeof(byte[])), "TD byte[] (no context)"); + Assert.True(_icoConvFrmTD.CanConvertFrom(null, typeof(byte[])), "TD byte[]"); + Assert.True(_icoConvFrmTD.CanConvertFrom(null, _iconBytes.GetType()), "TD _iconBytes.GetType()"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(string)), "TD string"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(Rectangle)), "TD Rectangle"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(Point)), "TD Point"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(PointF)), "TD PointF"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(Size)), "TD Size"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(SizeF)), "TD SizeF"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(object)), "TD object"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(int)), "TD int"); + Assert.False(_icoConvFrmTD.CanConvertFrom(null, typeof(Metafile)), "TD Metafile"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertTo() + { + Assert.True(_icoConv.CanConvertTo(typeof(string)), "string (no context)"); + Assert.True(_icoConv.CanConvertTo(null, typeof(string)), "string"); + Assert.True(_icoConv.CanConvertTo(null, _iconStr.GetType()), "_iconStr.GetType()"); + Assert.True(_icoConv.CanConvertTo(typeof(byte[])), "byte[] (no context)"); + Assert.True(_icoConv.CanConvertTo(null, typeof(byte[])), "byte[]"); + Assert.True(_icoConv.CanConvertTo(null, _iconBytes.GetType()), "_iconBytes.GetType()"); + Assert.True(_icoConv.CanConvertTo(typeof(Image)), "Image (no context)"); + Assert.True(_icoConv.CanConvertTo(null, typeof(Image)), "Image"); + Assert.True(_icoConv.CanConvertTo(typeof(Bitmap)), "Bitmap (no context)"); + Assert.True(_icoConv.CanConvertTo(null, typeof(Bitmap)), "Bitmap"); + Assert.False(_icoConv.CanConvertTo(null, typeof(Rectangle)), "Rectangle"); + Assert.False(_icoConv.CanConvertTo(null, typeof(Point)), "Point"); + Assert.False(_icoConv.CanConvertTo(null, typeof(PointF)), "PointF"); + Assert.False(_icoConv.CanConvertTo(null, typeof(Size)), "Size"); + Assert.False(_icoConv.CanConvertTo(null, typeof(SizeF)), "SizeF"); + Assert.False(_icoConv.CanConvertTo(null, typeof(object)), "object"); + Assert.False(_icoConv.CanConvertTo(null, typeof(int)), "int"); + + Assert.True(_icoConvFrmTD.CanConvertTo(typeof(string)), "TD string (no context)"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, typeof(string)), "TD string"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, _iconStr.GetType()), "TD _iconStr.GetType()"); + Assert.True(_icoConvFrmTD.CanConvertTo(typeof(byte[])), "TD byte[] (no context)"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, typeof(byte[])), "TD byte[]"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, _iconBytes.GetType()), "TD _iconBytes.GetType()"); + Assert.True(_icoConvFrmTD.CanConvertTo(typeof(Image)), "TD Image (no context)"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, typeof(Image)), "TD Image"); + Assert.True(_icoConvFrmTD.CanConvertTo(typeof(Bitmap)), "TD Bitmap (no context)"); + Assert.True(_icoConvFrmTD.CanConvertTo(null, typeof(Bitmap)), "TD Bitmap"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(Rectangle)), "TD Rectangle"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(Point)), "TD Point"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(PointF)), "TD PointF"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(Size)), "TD Size"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(SizeF)), "TD SizeF"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(object)), "TD object"); + Assert.False(_icoConvFrmTD.CanConvertTo(null, typeof(int)), "TD int"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertFrom() + { + Icon newIcon = (Icon)_icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, _iconBytes); + + Assert.Equal(_icon.Height, newIcon.Height); + Assert.Equal(_icon.Width, newIcon.Width); + + Assert.Throws(() => _icoConv.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, new Bitmap(20, 20))); + Assert.Throws(() => _icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, new Point(10, 10))); + Assert.Throws(() => _icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, new SizeF(10, 10))); + Assert.Throws(() => _icoConv.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + + + newIcon = (Icon)_icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, _iconBytes); + + Assert.Equal(_icon.Height, newIcon.Height); + Assert.Equal(_icon.Width, newIcon.Width); + + Assert.Throws(() => _icoConvFrmTD.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new Bitmap(20, 20))); + Assert.Throws(() => _icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new Point(10, 10))); + Assert.Throws(() => _icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new SizeF(10, 10))); + Assert.Throws(() => _icoConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertTo() + { + Assert.Equal(_iconStr, (string)_icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(string))); + Assert.Equal(_iconStr, (string)_icoConv.ConvertTo(_icon, typeof(string))); + + byte[] newIconBytes = (byte[])_icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, _iconBytes.GetType()); + Assert.Equal(_iconBytes, newIconBytes); + + newIconBytes = (byte[])_icoConv.ConvertTo(_icon, _iconBytes.GetType()); + Assert.Equal(_iconBytes, newIconBytes); + + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Rectangle))); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, _icon.GetType())); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Size))); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Point))); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Metafile))); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(object))); + Assert.Throws(() => _icoConv.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(int))); + + Assert.Equal(_iconStr, (string)_icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(string))); + Assert.Equal(_iconStr, (string)_icoConvFrmTD.ConvertTo(_icon, typeof(string))); + + + newIconBytes = (byte[])_icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, _iconBytes.GetType()); + Assert.Equal(_iconBytes, newIconBytes); + + newIconBytes = (byte[])_icoConvFrmTD.ConvertTo(_icon, _iconBytes.GetType()); + Assert.Equal(_iconBytes, newIconBytes); + + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Rectangle))); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, _icon.GetType())); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Size))); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Point))); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(Metafile))); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(object))); + Assert.Throws(() => _icoConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _icon, typeof(int))); + + using (new ThreadCultureChange(CultureInfo.CreateSpecificCulture("fr-FR"), CultureInfo.InvariantCulture)) + { + Assert.Equal("(none)", (string)_icoConv.ConvertTo(null, typeof(string))); + Assert.Equal("(none)", (string)_icoConv.ConvertTo(null, CultureInfo.CreateSpecificCulture("ru-RU"), null, typeof(string))); + + Assert.Equal("(none)", (string)_icoConvFrmTD.ConvertTo(null, typeof(string))); + Assert.Equal("(none)", (string)_icoConvFrmTD.ConvertTo(null, CultureInfo.CreateSpecificCulture("de-DE"), null, typeof(string))); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs b/src/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs new file mode 100644 index 00000000000..a7abefdcb39 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/ImageAnimator.ManualTests.cs @@ -0,0 +1,87 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing.Imaging; +using System.IO; +using System.Threading; +using Xunit; + +namespace System.Drawing.Tests +{ + public class ImageAnimatorManualTests + { + public static bool ManualTestsEnabled => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MANUAL_TESTS")); + public static string OutputFolder = Path.Combine(Environment.CurrentDirectory, "ImageAnimatorManualTests", DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss")); + + // To run these tests, change the working directory to src/libraries/System.Drawing.Common, + // set the `MANUAL_TESTS` environment variable to any non-empty value, and run + // `dotnet test --filter "ImageAnimatorManualTests" + + [ConditionalFact(Helpers.IsDrawingSupported, nameof(ManualTestsEnabled), Timeout = 75_000)] + public void AnimateAndCaptureFrames() + { + // This test animates the test gifs that we have and waits 60 seconds + // for the animations to progress. As the frame change events occur, we + // capture snapshots of the current frame, essentially extracting the + // frames from the GIF. + + // The animation should progress at the expected pace to stay synchronized + // with the wall clock, and the animated timer images show the time duration + // within the image itself, so this can be manually verified for accuracy. + + // The captured frames are stored in the `artifacts/bin/System.Drawing.Common.Tests` + // folder for each configuration, and then under an `ImageAnimatorManualTests` folder + // with a timestamped folder under that. Each animation image gets its own folder too. + + string[] images = new string[] + { + "animated-timer-1fps-repeat-2.gif", + "animated-timer-1fps-repeat-infinite.gif", + "animated-timer-10fps-repeat-2.gif", + "animated-timer-10fps-repeat-infinite.gif", + "animated-timer-100fps-repeat-2.gif", + "animated-timer-100fps-repeat-infinite.gif", + "animated-timer-0-delay-all-frames.gif", + }; + + Dictionary handlers = new(); + Dictionary frameIndexes = new(); + Dictionary bitmaps = new(); + + Stopwatch stopwatch = new(); + + foreach (var imageName in images) + { + string testOutputFolder = Path.Combine(OutputFolder, Path.GetFileNameWithoutExtension(imageName)); + Directory.CreateDirectory(testOutputFolder); + frameIndexes[imageName] = 0; + + handlers[imageName] = new EventHandler(new Action((object o, EventArgs e) => + { + Bitmap animation = (Bitmap)o; + ImageAnimator.UpdateFrames(animation); + + // We save captures using jpg so that: + // a) The images don't get saved as animated gifs again, and just a single frame is saved + // b) Saving pngs in this test on Linux was leading to sporadic GDI+ errors; Jpeg is more reliable + string timestamp = stopwatch.ElapsedMilliseconds.ToString("000000"); + animation.Save(Path.Combine(testOutputFolder, $"{++frameIndexes[imageName]}_{timestamp}.jpg"), ImageFormat.Jpeg); + })); + + bitmaps[imageName] = new Bitmap(Helpers.GetTestBitmapPath(imageName)); + ImageAnimator.Animate(bitmaps[imageName], handlers[imageName]); + } + + stopwatch.Start(); + Thread.Sleep(60_000); + + foreach (var imageName in images) + { + ImageAnimator.StopAnimate(bitmaps[imageName], handlers[imageName]); + bitmaps[imageName].Dispose(); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/ImageAnimatorTests.cs b/src/System.Drawing.Common/tests/System/Drawing/ImageAnimatorTests.cs new file mode 100644 index 00000000000..49925d47631 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/ImageAnimatorTests.cs @@ -0,0 +1,158 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace System.Drawing.Tests +{ + public class ImageAnimatorTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_WithNothingAnimating() + { + ImageAnimator.UpdateFrames(); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("1bit.png")] + [InlineData("48x48_one_entry_1bit.ico")] + [InlineData("81773-interlaced.gif")] + public void CanAnimate_ReturnsFalse_ForNonAnimatedImages(string imageName) + { + using (var image = new Bitmap(Helpers.GetTestBitmapPath(imageName))) + { + Assert.False(ImageAnimator.CanAnimate(image)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Animate_Succeeds_ForNonAnimatedImages_WithNothingAnimating() + { + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.Animate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Animate_Succeeds_ForNonAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.Animate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_ForNonAnimatedImages_WithNothingAnimating() + { + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.UpdateFrames(image); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_ForNonAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.UpdateFrames(image); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StopAnimate_Succeeds_ForNonAnimatedImages_WithNothingAnimating() + { + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.StopAnimate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StopAnimate_Succeeds_ForNonAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + + var image = new Bitmap(Helpers.GetTestBitmapPath("1bit.png")); + ImageAnimator.StopAnimate(image, (object o, EventArgs e) => { }); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("animated-timer-1fps-repeat-2.gif")] + [InlineData("animated-timer-1fps-repeat-infinite.gif")] + [InlineData("animated-timer-10fps-repeat-2.gif")] + [InlineData("animated-timer-10fps-repeat-infinite.gif")] + [InlineData("animated-timer-100fps-repeat-2.gif")] + [InlineData("animated-timer-100fps-repeat-infinite.gif")] + public void CanAnimate_ReturnsTrue_ForAnimatedImages(string imageName) + { + using (var image = new Bitmap(Helpers.GetTestBitmapPath(imageName))) + { + Assert.True(ImageAnimator.CanAnimate(image)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Animate_Succeeds_ForAnimatedImages_WithNothingAnimating() + { + var image = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Animate_Succeeds_ForAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + + var image = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-infinite.gif")); + ImageAnimator.Animate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_ForAnimatedImages_WithNothingAnimating() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.UpdateFrames(animatedImage); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + ImageAnimator.UpdateFrames(); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void UpdateFrames_Succeeds_ForAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + ImageAnimator.UpdateFrames(animatedImage); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StopAnimate_Succeeds_ForAnimatedImages_WithNothingAnimating() + { + var image = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.StopAnimate(image, (object o, EventArgs e) => { }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void StopAnimate_Succeeds_ForAnimatedImages_WithCurrentAnimations() + { + var animatedImage = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-2.gif")); + ImageAnimator.Animate(animatedImage, (object o, EventArgs e) => { }); + + var image = new Bitmap(Helpers.GetTestBitmapPath("animated-timer-100fps-repeat-infinite.gif")); + ImageAnimator.StopAnimate(animatedImage, (object o, EventArgs e) => { }); + ImageAnimator.StopAnimate(image, (object o, EventArgs e) => { }); + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/ImageConverterTests.cs b/src/System.Drawing.Common/tests/System/Drawing/ImageConverterTests.cs new file mode 100644 index 00000000000..ecae7560dab --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/ImageConverterTests.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.Drawing.Imaging; +using System.Globalization; +using System.IO; +using Xunit; + +namespace System.ComponentModel.TypeConverterTests +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/34755", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class ImageConverterTest + { + private readonly Image _image; + private readonly ImageConverter _imgConv; + private readonly ImageConverter _imgConvFrmTD; + private readonly string _imageStr; + private readonly byte[] _imageBytes; + + public ImageConverterTest() + { + _image = Image.FromFile(Path.Combine("bitmaps", "TestImage.bmp")); + _imageStr = _image.ToString(); + + using (MemoryStream destStream = new MemoryStream()) + { + _image.Save(destStream, _image.RawFormat); + _imageBytes = destStream.ToArray(); + } + + _imgConv = new ImageConverter(); + _imgConvFrmTD = (ImageConverter)TypeDescriptor.GetConverter(_image); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("48x48_multiple_entries_4bit.ico")] + [InlineData("256x256_seven_entries_multiple_bits.ico")] + [InlineData("pngwithheight_icon.ico")] + public void ImageConverterFromIconTest(string name) + { + using (var icon = new Icon(Helpers.GetTestBitmapPath(name))) + { + Bitmap IconBitmap = (Bitmap)_imgConv.ConvertFrom(icon); + Assert.NotNull(IconBitmap); + Assert.Equal(32, IconBitmap.Width); + Assert.Equal(32, IconBitmap.Height); + Assert.Equal(new Size(32, 32), IconBitmap.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ImageWithOleHeader() + { + string path = Path.Combine("bitmaps", "TestImageWithOleHeader.bmp"); + using (FileStream fileStream = File.Open(path, FileMode.Open)) + { + using (var ms = new MemoryStream()) + { + fileStream.CopyTo(ms); + var converter = new ImageConverter(); + object image = converter.ConvertFrom(ms.ToArray()); + Assert.NotNull(image); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertFrom() + { + Assert.True(_imgConv.CanConvertFrom(typeof(byte[])), "byte[] (no context)"); + Assert.True(_imgConv.CanConvertFrom(null, typeof(byte[])), "byte[]"); + Assert.True(_imgConv.CanConvertFrom(null, _imageBytes.GetType()), "_imageBytes.GetType()"); + Assert.True(_imgConv.CanConvertFrom(typeof(Icon)), "Icon (no context)"); + Assert.True(_imgConv.CanConvertFrom(null, typeof(Icon)), "Icon"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(string)), "string"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(Rectangle)), "Rectangle"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(Point)), "Point"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(PointF)), "PointF"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(Size)), "Size"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(SizeF)), "SizeF"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(object)), "object"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(int)), "int"); + Assert.False(_imgConv.CanConvertFrom(null, typeof(Metafile)), "Mefafile"); + + Assert.True(_imgConvFrmTD.CanConvertFrom(typeof(byte[])), "TD byte[] (no context)"); + Assert.True(_imgConvFrmTD.CanConvertFrom(null, typeof(byte[])), "TD byte[]"); + Assert.True(_imgConvFrmTD.CanConvertFrom(null, _imageBytes.GetType()), "TD _imageBytes.GetType()"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(string)), "TD string"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(Rectangle)), "TD Rectangle"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(Point)), "TD Point"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(PointF)), "TD PointF"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(Size)), "TD Size"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(SizeF)), "TD SizeF"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(object)), "TD object"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(int)), "TD int"); + Assert.False(_imgConvFrmTD.CanConvertFrom(null, typeof(Metafile)), "TD Metafile"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertTo() + { + Assert.True(_imgConv.CanConvertTo(typeof(string)), "stirng (no context)"); + Assert.True(_imgConv.CanConvertTo(null, typeof(string)), "string"); + Assert.True(_imgConv.CanConvertTo(null, _imageStr.GetType()), "_imageStr.GetType()"); + Assert.True(_imgConv.CanConvertTo(typeof(byte[])), "byte[] (no context)"); + Assert.True(_imgConv.CanConvertTo(null, typeof(byte[])), "byte[]"); + Assert.True(_imgConv.CanConvertTo(null, _imageBytes.GetType()), "_imageBytes.GetType()"); + Assert.False(_imgConv.CanConvertTo(null, typeof(Rectangle)), "Rectangle"); + Assert.False(_imgConv.CanConvertTo(null, typeof(Point)), "Point"); + Assert.False(_imgConv.CanConvertTo(null, typeof(PointF)), "PointF"); + Assert.False(_imgConv.CanConvertTo(null, typeof(Size)), "Size"); + Assert.False(_imgConv.CanConvertTo(null, typeof(SizeF)), "SizeF"); + Assert.False(_imgConv.CanConvertTo(null, typeof(object)), "object"); + Assert.False(_imgConv.CanConvertTo(null, typeof(int)), "int"); + + Assert.True(_imgConvFrmTD.CanConvertTo(typeof(string)), "TD string (no context)"); + Assert.True(_imgConvFrmTD.CanConvertTo(null, typeof(string)), "TD string"); + Assert.True(_imgConvFrmTD.CanConvertTo(null, _imageStr.GetType()), "TD _imageStr.GetType()"); + Assert.True(_imgConvFrmTD.CanConvertTo(typeof(byte[])), "TD byte[] (no context)"); + Assert.True(_imgConvFrmTD.CanConvertTo(null, typeof(byte[])), "TD byte[]"); + Assert.True(_imgConvFrmTD.CanConvertTo(null, _imageBytes.GetType()), "TD _imageBytes.GetType()"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(Rectangle)), "TD Rectangle"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(Point)), "TD Point"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(PointF)), "TD PointF"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(Size)), "TD Size"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(SizeF)), "TD SizeF"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(object)), "TD object"); + Assert.False(_imgConvFrmTD.CanConvertTo(null, typeof(int)), "TD int"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertFrom() + { + Image newImage = (Image)_imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, _imageBytes); + + Assert.Equal(_image.Height, newImage.Height); + Assert.Equal(_image.Width, newImage.Width); + + newImage = (Image)_imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, _imageBytes); + + Assert.Equal(_image.Height, newImage.Height); + Assert.Equal(_image.Width, newImage.Width); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertFrom_ThrowsNotSupportedException() + { + Assert.Throws(() => _imgConv.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, new Bitmap(20, 20))); + Assert.Throws(() => _imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, new Point(10, 10))); + Assert.Throws(() => _imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, new SizeF(10, 10))); + Assert.Throws(() => _imgConv.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + + Assert.Throws(() => _imgConvFrmTD.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new Bitmap(20, 20))); + Assert.Throws(() => _imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new Point(10, 10))); + Assert.Throws(() => _imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new SizeF(10, 10))); + Assert.Throws(() => _imgConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertTo_String() + { + Assert.Equal(_imageStr, (string)_imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(string))); + Assert.Equal(_imageStr, (string)_imgConv.ConvertTo(_image, typeof(string))); + Assert.Equal(_imageStr, (string)_imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(string))); + Assert.Equal(_imageStr, (string)_imgConvFrmTD.ConvertTo(_image, typeof(string))); + + using (new ThreadCultureChange(CultureInfo.CreateSpecificCulture("fr-FR"), CultureInfo.InvariantCulture)) + { + Assert.Equal("(none)", (string)_imgConv.ConvertTo(null, typeof(string))); + Assert.Equal("(none)", (string)_imgConv.ConvertTo(null, CultureInfo.CreateSpecificCulture("ru-RU"), null, typeof(string))); + + Assert.Equal("(none)", (string)_imgConvFrmTD.ConvertTo(null, typeof(string))); + Assert.Equal("(none)", (string)_imgConvFrmTD.ConvertTo(null, CultureInfo.CreateSpecificCulture("de-DE"), null, typeof(string))); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertTo_ByteArray() + { + byte[] newImageBytes = (byte[])_imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, _imageBytes.GetType()); + Assert.Equal(_imageBytes, newImageBytes); + + newImageBytes = (byte[])_imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, _imageBytes.GetType()); + Assert.Equal(_imageBytes, newImageBytes); + + newImageBytes = (byte[])_imgConvFrmTD.ConvertTo(_image, _imageBytes.GetType()); + Assert.Equal(_imageBytes, newImageBytes); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertTo_FromBitmapToByteArray() + { + Bitmap value = new Bitmap(64, 64); + ImageConverter converter = new ImageConverter(); + byte[] converted = (byte[])converter.ConvertTo(value, typeof(byte[])); + Assert.NotNull(converted); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertTo_ThrowsNotSupportedException() + { + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Rectangle))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, _image.GetType())); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Size))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Bitmap))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Point))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Metafile))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(object))); + Assert.Throws(() => _imgConv.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(int))); + + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Rectangle))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, _image.GetType())); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Size))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Bitmap))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Point))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(Metafile))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(object))); + Assert.Throws(() => _imgConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _image, typeof(int))); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestGetPropertiesSupported() + { + Assert.True(_imgConv.GetPropertiesSupported(), "GetPropertiesSupported()"); + Assert.True(_imgConv.GetPropertiesSupported(null), "GetPropertiesSupported(null)"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestGetProperties() + { + const int allPropertiesCount = 14; // Count of all properties in Image class. + const int browsablePropertiesCount = 7; // Count of browsable properties in Image class (BrowsableAttribute.Yes). + + PropertyDescriptorCollection propsColl; + + // Internally calls TypeDescriptor.GetProperties(typeof(Image), null), which returns all properties. + propsColl = _imgConv.GetProperties(null, _image, null); + Assert.Equal(allPropertiesCount, propsColl.Count); + + // Internally calls TypeDescriptor.GetProperties(typeof(Image), new Attribute[] { BrowsableAttribute.Yes }). + propsColl = _imgConv.GetProperties(null, _image); + Assert.Equal(browsablePropertiesCount, propsColl.Count); + propsColl = _imgConv.GetProperties(_image); + Assert.Equal(browsablePropertiesCount, propsColl.Count); + + + // Returns all properties of Image class. + propsColl = TypeDescriptor.GetProperties(typeof(Image)); + Assert.Equal(allPropertiesCount, propsColl.Count); + + // Internally calls TypeDescriptor.GetProperties(typeof(Image), null), which returns all properties. + propsColl = _imgConvFrmTD.GetProperties(null, _image, null); + Assert.Equal(allPropertiesCount, propsColl.Count); + + // Internally calls TypeDescriptor.GetProperties(typeof(Image), new Attribute[] { BrowsableAttribute.Yes }). + propsColl = _imgConvFrmTD.GetProperties(null, _image); + Assert.Equal(browsablePropertiesCount, propsColl.Count); + propsColl = _imgConvFrmTD.GetProperties(_image); + Assert.Equal(browsablePropertiesCount, propsColl.Count); + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/ImageFormatConverterTests.cs b/src/System.Drawing.Common/tests/System/Drawing/ImageFormatConverterTests.cs new file mode 100644 index 00000000000..7935b0adfe8 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/ImageFormatConverterTests.cs @@ -0,0 +1,254 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Drawing; +using System.Drawing.Imaging; +using System.Globalization; +using Xunit; + +namespace System.ComponentModel.TypeConverterTests +{ + public class ImageFormatConverterTest + { + private readonly ImageFormat _imageFmt; + private readonly ImageFormatConverter _imgFmtConv; + private readonly ImageFormatConverter _imgFmtConvFrmTD; + private readonly string _imageFmtStr; + + public ImageFormatConverterTest() + { + _imageFmt = ImageFormat.Bmp; + _imageFmtStr = _imageFmt.ToString(); + + _imgFmtConv = new ImageFormatConverter(); + _imgFmtConvFrmTD = (ImageFormatConverter)TypeDescriptor.GetConverter(_imageFmt); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertFrom() + { + Assert.True(_imgFmtConv.CanConvertFrom(typeof(string)), "string (no context)"); + Assert.True(_imgFmtConv.CanConvertFrom(null, typeof(string)), "string"); + Assert.False(_imgFmtConv.CanConvertFrom(null, typeof(ImageFormat)), "ImageFormat"); + Assert.False(_imgFmtConv.CanConvertFrom(null, typeof(Guid)), "Guid"); + Assert.False(_imgFmtConv.CanConvertFrom(null, typeof(object)), "object"); + Assert.False(_imgFmtConv.CanConvertFrom(null, typeof(int)), "int"); + + Assert.True(_imgFmtConvFrmTD.CanConvertFrom(typeof(string)), "TD string (no context)"); + Assert.True(_imgFmtConvFrmTD.CanConvertFrom(null, typeof(string)), "TD string"); + Assert.False(_imgFmtConvFrmTD.CanConvertFrom(null, typeof(ImageFormat)), "TD ImageFormat"); + Assert.False(_imgFmtConvFrmTD.CanConvertFrom(null, typeof(Guid)), "TD Guid"); + Assert.False(_imgFmtConvFrmTD.CanConvertFrom(null, typeof(object)), "TD object"); + Assert.False(_imgFmtConvFrmTD.CanConvertFrom(null, typeof(int)), "TD int"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestCanConvertTo() + { + Assert.True(_imgFmtConv.CanConvertTo(typeof(string)), "string (no context)"); + Assert.True(_imgFmtConv.CanConvertTo(null, typeof(string)), "string"); + Assert.False(_imgFmtConv.CanConvertTo(null, typeof(ImageFormat)), "ImageFormat"); + Assert.False(_imgFmtConv.CanConvertTo(null, typeof(Guid)), "Guid"); + Assert.False(_imgFmtConv.CanConvertTo(null, typeof(object)), "object"); + Assert.False(_imgFmtConv.CanConvertTo(null, typeof(int)), "int"); + + Assert.True(_imgFmtConvFrmTD.CanConvertTo(typeof(string)), "TD string (no context)"); + Assert.True(_imgFmtConvFrmTD.CanConvertTo(null, typeof(string)), "TD string"); + Assert.False(_imgFmtConvFrmTD.CanConvertTo(null, typeof(ImageFormat)), "TD ImageFormat"); + Assert.False(_imgFmtConvFrmTD.CanConvertTo(null, typeof(Guid)), "TD Guid"); + Assert.False(_imgFmtConvFrmTD.CanConvertTo(null, typeof(object)), "TD object"); + Assert.False(_imgFmtConvFrmTD.CanConvertTo(null, typeof(int)), "TD int"); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertFrom_ImageFormatToString() + { + Assert.Equal(_imageFmt, (ImageFormat)_imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp.ToString())); + Assert.Equal(_imageFmt, (ImageFormat)_imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp.ToString())); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertFrom_ThrowsNotSupportedException() + { + Assert.Throws(() => _imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp)); + Assert.Throws(() => _imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp.Guid)); + Assert.Throws(() => _imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + Assert.Throws(() => _imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, 10)); + + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp)); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, ImageFormat.Bmp.Guid)); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, new object())); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, 10)); + } + + private ImageFormat ConvertFromName(string imgFormatName) + { + return (ImageFormat)_imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, imgFormatName); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ConvertFrom_ShortName() + { + Assert.Equal(ImageFormat.Bmp, ConvertFromName("Bmp")); + Assert.Equal(ImageFormat.Emf, ConvertFromName("Emf")); + Assert.Equal(ImageFormat.Exif, ConvertFromName("Exif")); + Assert.Equal(ImageFormat.Gif, ConvertFromName("Gif")); + Assert.Equal(ImageFormat.Tiff, ConvertFromName("Tiff")); + Assert.Equal(ImageFormat.Png, ConvertFromName("Png")); + Assert.Equal(ImageFormat.MemoryBmp, ConvertFromName("MemoryBmp")); + Assert.Equal(ImageFormat.Icon, ConvertFromName("Icon")); + Assert.Equal(ImageFormat.Jpeg, ConvertFromName("Jpeg")); + Assert.Equal(ImageFormat.Wmf, ConvertFromName("Wmf")); +#if NET + Assert.Equal(ImageFormat.Heif, ConvertFromName("Heif")); + Assert.Equal(ImageFormat.Webp, ConvertFromName("Webp")); +#endif + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Support to convert image format from long name was added to .NET Core directly.")] + public void ConvertFrom_LongName() + { + Guid testGuid = Guid.NewGuid(); + ImageFormat imageformat = ConvertFromName($"[ImageFormat: {testGuid}]"); + Assert.Equal(testGuid, imageformat.Guid); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Support to convert image format from long name was added to .NET Core directly.")] + public void ConvertFrom_ThrowsFormatExceptionOnInvalidFormatString() + { + Assert.Throws(() => _imgFmtConv.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _imgFmtConv.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _imgFmtConv.ConvertFrom("[ImageFormat: abcdefgh-ijkl-mnop-qrst-uvwxyz012345]")); + + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom("System.Drawing.String")); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom(null, CultureInfo.InvariantCulture, "System.Drawing.String")); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertFrom("[ImageFormat: abcdefgh-ijkl-mnop-qrst-uvwxyz012345]")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertTo_String() + { + Assert.Equal(_imageFmtStr, (string)_imgFmtConv.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(string))); + Assert.Equal(_imageFmtStr, (string)_imgFmtConv.ConvertTo(_imageFmt, typeof(string))); + + Assert.Equal(_imageFmtStr, (string)_imgFmtConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(string))); + Assert.Equal(_imageFmtStr, (string)_imgFmtConvFrmTD.ConvertTo(_imageFmt, typeof(string))); + + Assert.Equal(string.Empty, (string)_imgFmtConv.ConvertTo(null, typeof(string))); + Assert.Equal(string.Empty, (string)_imgFmtConv.ConvertTo(null, CultureInfo.CreateSpecificCulture("ru-RU"), null, typeof(string))); + + Assert.Equal(string.Empty, (string)_imgFmtConvFrmTD.ConvertTo(null, typeof(string))); + Assert.Equal(string.Empty, (string)_imgFmtConvFrmTD.ConvertTo(null, CultureInfo.CreateSpecificCulture("de-DE"), null, typeof(string))); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestConvertTo_ThrowsNotSupportedException() + { + Assert.Throws(() => _imgFmtConv.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(ImageFormat))); + Assert.Throws(() => _imgFmtConv.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(Guid))); + Assert.Throws(() => _imgFmtConv.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(object))); + Assert.Throws(() => _imgFmtConv.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(int))); + + Assert.Throws(() => _imgFmtConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(ImageFormat))); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(Guid))); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(object))); + Assert.Throws(() => _imgFmtConvFrmTD.ConvertTo(null, CultureInfo.InvariantCulture, _imageFmt, typeof(int))); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetStandardValuesSupported() + { + Assert.True(_imgFmtConv.GetStandardValuesSupported(), "GetStandardValuesSupported()"); + Assert.True(_imgFmtConv.GetStandardValuesSupported(null), "GetStandardValuesSupported(null)"); + } + + private void CheckStandardValues(ICollection values) + { + bool memorybmp = false; + bool bmp = false; + bool emf = false; + bool wmf = false; + bool gif = false; + bool jpeg = false; + bool png = false; + bool tiff = false; + bool exif = false; + bool icon = false; +#if NET + bool heif = false; + bool webp = false; +#endif + + foreach (ImageFormat iformat in values) + { + switch (iformat.Guid.ToString()) + { + case "b96b3caa-0728-11d3-9d7b-0000f81ef32e": + memorybmp = true; + break; + case "b96b3cab-0728-11d3-9d7b-0000f81ef32e": + bmp = true; + break; + case "b96b3cac-0728-11d3-9d7b-0000f81ef32e": + emf = true; + break; + case "b96b3cad-0728-11d3-9d7b-0000f81ef32e": + wmf = true; + break; + case "b96b3cb0-0728-11d3-9d7b-0000f81ef32e": + gif = true; + break; + case "b96b3cae-0728-11d3-9d7b-0000f81ef32e": + jpeg = true; + break; + case "b96b3caf-0728-11d3-9d7b-0000f81ef32e": + png = true; + break; + case "b96b3cb1-0728-11d3-9d7b-0000f81ef32e": + tiff = true; + break; + case "b96b3cb2-0728-11d3-9d7b-0000f81ef32e": + exif = true; + break; + case "b96b3cb5-0728-11d3-9d7b-0000f81ef32e": + icon = true; + break; +#if NET + case "b96b3cb6-0728-11d3-9d7b-0000f81ef32e": + heif = true; + break; + case "b96b3cb7-0728-11d3-9d7b-0000f81ef32e": + webp = true; + break; +#endif + default: + throw new InvalidOperationException($"Unknown GUID {iformat.Guid}."); + } + } + Assert.True(memorybmp, "MemoryBMP"); + Assert.True(bmp, "Bmp"); + Assert.True(emf, "Emf"); + Assert.True(wmf, "Wmf"); + Assert.True(gif, "Gif"); + Assert.True(jpeg, "Jpeg"); + Assert.True(png, "Png"); + Assert.True(tiff, "Tiff"); + Assert.True(exif, "Exif"); + Assert.True(icon, "Icon"); +#if NET + Assert.True(heif, "Heif"); + Assert.True(webp, "Webp"); +#endif + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetStandardValues() + { + CheckStandardValues(_imgFmtConv.GetStandardValues()); + CheckStandardValues(_imgFmtConv.GetStandardValues(null)); + } + } +} diff --git a/src/System.Drawing.Common/tests/System/Drawing/Printing/MarginsConverterTests.cs b/src/System.Drawing.Common/tests/System/Drawing/Printing/MarginsConverterTests.cs new file mode 100644 index 00000000000..197691ccd12 --- /dev/null +++ b/src/System.Drawing.Common/tests/System/Drawing/Printing/MarginsConverterTests.cs @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Globalization; +using Xunit; + +namespace System.Drawing.Printing.Tests +{ + public class MarginsConverterTests + { + [Fact] + public void CanConvertFrom() + { + MarginsConverter mc = new MarginsConverter(); + + // try once with then once without context + for (var context = new MyTypeDescriptorContext(); context != null; context = null) + { + Assert.True(mc.CanConvertFrom(context, typeof(string))); + Assert.False(mc.CanConvertFrom(context, typeof(Guid))); + Assert.False(mc.CanConvertFrom(context, typeof(object))); + Assert.False(mc.CanConvertFrom(context, typeof(int))); + } + } + + [Fact] + public void CanConvertTo() + { + MarginsConverter mc = new MarginsConverter(); + + // try once with then once without context + for (var context = new MyTypeDescriptorContext(); context != null; context = null) + { + Assert.True(mc.CanConvertTo(context, typeof(string))); + Assert.False(mc.CanConvertTo(context, typeof(Guid))); + Assert.False(mc.CanConvertTo(context, typeof(object))); + Assert.False(mc.CanConvertTo(context, typeof(int))); + } + } + + [Fact] + public void CreateInstance() + { + MarginsConverter mc = new MarginsConverter(); + MyTypeDescriptorContext context = new MyTypeDescriptorContext(); + + IDictionary values = new Dictionary(); + values.Add("Left", 1); + values.Add("Right", 2); + values.Add("Top", 3); + Assert.Throws(() => mc.CreateInstance(context, values)); + values.Add("Bottom", 4); + + object result = mc.CreateInstance(context, values); + Assert.NotNull(result); + + Assert.IsType(result); + Margins margins = result as Margins; + Assert.Equal(1, margins.Left); + Assert.Equal(2, margins.Right); + Assert.Equal(3, margins.Top); + Assert.Equal(4, margins.Bottom); + } + + [Fact] + public void GetCreateInstanceSupported() + { + MarginsConverter mc = new MarginsConverter(); + Assert.True(mc.GetCreateInstanceSupported(null)); + Assert.True(mc.GetCreateInstanceSupported(new MyTypeDescriptorContext())); + } + + [Fact] + public void ConvertFrom() + { + MarginsConverter mc = new MarginsConverter(); + CultureInfo culture = CultureInfo.InvariantCulture; + + // try once with then once without context + for (var context = new MyTypeDescriptorContext(); context != null; context = null) + { + object result; + Assert.Equal(',', culture.TextInfo.ListSeparator[0]); + AssertExtensions.Throws(() => mc.ConvertFrom(context, culture, "1;2;3;4")); + result = mc.ConvertFrom(context, culture, "1,2,3,4"); + Assert.IsType(result); + Margins margins = result as Margins; + Assert.Equal(1, margins.Left); + Assert.Equal(2, margins.Right); + Assert.Equal(3, margins.Top); + Assert.Equal(4, margins.Bottom); + } + } + + [Fact] + public void ConvertFrom_Throws() + { + MarginsConverter mc = new MarginsConverter(); + CultureInfo culture = CultureInfo.InvariantCulture; + + // try once with then once without context + for (var context = new MyTypeDescriptorContext(); context != null; context = null) + { + Assert.Throws(() => mc.ConvertFrom(context, null, null)); + Assert.Throws(() => mc.ConvertFrom(context, culture, null)); + Assert.Throws(() => mc.ConvertFrom(context, culture, Guid.NewGuid())); + AssertExtensions.Throws(() => mc.ConvertFrom(context, null, "wrong string format")); + AssertExtensions.Throws(() => mc.ConvertFrom(context, culture, "wrong string format")); + } + } + + [Fact] + public void ConvertTo() + { + MarginsConverter mc = new MarginsConverter(); + Guid guid = Guid.NewGuid(); + CultureInfo culture = CultureInfo.InvariantCulture; + Margins margins = new Margins() { Left = 1, Right = 2, Top = 3, Bottom = 4 }; + + // try once with then once without context + for (var context = new MyTypeDescriptorContext(); context != null; context = null) + { + Assert.Equal("1;2;3;4", mc.ConvertTo(context, culture, "1;2;3;4", typeof(string))); + + object converted = mc.ConvertTo(context, culture, margins, typeof(string)); + Assert.IsType(converted); + Assert.Equal(',', culture.TextInfo.ListSeparator[0]); + Assert.Equal("1, 2, 3, 4", converted); + + converted = mc.ConvertTo(context, culture, margins, typeof(InstanceDescriptor)); + Assert.IsType(converted); + Assert.Equal(new object[] { 1, 2, 3, 4 }, ((InstanceDescriptor)converted).Arguments); + + Assert.Throws(() => mc.ConvertTo(context, culture, new object(), typeof(object))); + Assert.Throws(() => mc.ConvertTo(context, culture, 12, typeof(int))); + Assert.Throws(() => mc.ConvertTo(context, culture, guid, typeof(Guid))); + + Assert.Equal(string.Empty, (string)mc.ConvertTo(null, typeof(string))); + Assert.Equal(string.Empty, (string)mc.ConvertTo(context, CultureInfo.CreateSpecificCulture("ru-RU"), null, typeof(string))); + } + } + + private class MyTypeDescriptorContext : ITypeDescriptorContext + { + public IContainer Container => null; + public object Instance { get { return null; } } + public PropertyDescriptor PropertyDescriptor { get { return null; } } + public bool OnComponentChanging() { return true; } + public void OnComponentChanged() { } + public object GetService(Type serviceType) { return null; } + } + } +} diff --git a/src/System.Drawing.Common/tests/SystemBrushesTests.cs b/src/System.Drawing.Common/tests/SystemBrushesTests.cs new file mode 100644 index 00000000000..2533911fb84 --- /dev/null +++ b/src/System.Drawing.Common/tests/SystemBrushesTests.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Reflection; +using Xunit; + +namespace System.Drawing.Tests +{ + public class SystemBrushesTests + { + public static IEnumerable SystemBrushes_TestData() + { + yield return Brush(() => SystemBrushes.ActiveBorder, SystemColors.ActiveBorder); + yield return Brush(() => SystemBrushes.ActiveCaption, SystemColors.ActiveCaption); + yield return Brush(() => SystemBrushes.ActiveCaptionText, SystemColors.ActiveCaptionText); + yield return Brush(() => SystemBrushes.AppWorkspace, SystemColors.AppWorkspace); + yield return Brush(() => SystemBrushes.ButtonFace, SystemColors.ButtonFace); + yield return Brush(() => SystemBrushes.ButtonHighlight, SystemColors.ButtonHighlight); + yield return Brush(() => SystemBrushes.ButtonShadow, SystemColors.ButtonShadow); + yield return Brush(() => SystemBrushes.Control, SystemColors.Control); + yield return Brush(() => SystemBrushes.ControlDark, SystemColors.ControlDark); + yield return Brush(() => SystemBrushes.ControlDarkDark, SystemColors.ControlDarkDark); + yield return Brush(() => SystemBrushes.ControlLight, SystemColors.ControlLight); + yield return Brush(() => SystemBrushes.ControlLightLight, SystemColors.ControlLightLight); + yield return Brush(() => SystemBrushes.ControlText, SystemColors.ControlText); + yield return Brush(() => SystemBrushes.Desktop, SystemColors.Desktop); + yield return Brush(() => SystemBrushes.GradientActiveCaption, SystemColors.GradientActiveCaption); + yield return Brush(() => SystemBrushes.GradientInactiveCaption, SystemColors.GradientInactiveCaption); + yield return Brush(() => SystemBrushes.GrayText, SystemColors.GrayText); + yield return Brush(() => SystemBrushes.Highlight, SystemColors.Highlight); + yield return Brush(() => SystemBrushes.HighlightText, SystemColors.HighlightText); + yield return Brush(() => SystemBrushes.HotTrack, SystemColors.HotTrack); + yield return Brush(() => SystemBrushes.InactiveBorder, SystemColors.InactiveBorder); + yield return Brush(() => SystemBrushes.InactiveCaption, SystemColors.InactiveCaption); + yield return Brush(() => SystemBrushes.InactiveCaptionText, SystemColors.InactiveCaptionText); + yield return Brush(() => SystemBrushes.Info, SystemColors.Info); + yield return Brush(() => SystemBrushes.InfoText, SystemColors.InfoText); + yield return Brush(() => SystemBrushes.Menu, SystemColors.Menu); + yield return Brush(() => SystemBrushes.MenuBar, SystemColors.MenuBar); + yield return Brush(() => SystemBrushes.MenuHighlight, SystemColors.MenuHighlight); + yield return Brush(() => SystemBrushes.MenuText, SystemColors.MenuText); + yield return Brush(() => SystemBrushes.ScrollBar, SystemColors.ScrollBar); + yield return Brush(() => SystemBrushes.Window, SystemColors.Window); + yield return Brush(() => SystemBrushes.WindowFrame, SystemColors.WindowFrame); + yield return Brush(() => SystemBrushes.WindowText, SystemColors.WindowText); + } + + public static object[] Brush(Func getBrush, Color expectedColor) => new object[] { getBrush, expectedColor }; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SystemBrushes_TestData))] + public void SystemBrushes_Get_ReturnsExpected(Func getBrush, Color expectedColor) + { + SolidBrush brush = Assert.IsType(getBrush()); + Assert.Equal(expectedColor, brush.Color); + AssertExtensions.Throws(null, () => brush.Color = Color.Red); + + Assert.Same(brush, getBrush()); + } + + [Fact] + public void FromSystemColor_NotSystemColor_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => SystemBrushes.FromSystemColor(Color.Blue)); + } + } +} diff --git a/src/System.Drawing.Common/tests/SystemFontsTests.cs b/src/System.Drawing.Common/tests/SystemFontsTests.cs new file mode 100644 index 00000000000..0e7b71e7720 --- /dev/null +++ b/src/System.Drawing.Common/tests/SystemFontsTests.cs @@ -0,0 +1,175 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Tests +{ + public class SystemFontsTests + { + public static IEnumerable SystemFonts_TestData() + { + yield return new object[] { (Func)(() => SystemFonts.CaptionFont) }; + yield return new object[] { (Func)(() => SystemFonts.IconTitleFont) }; + yield return new object[] { (Func)(() => SystemFonts.MenuFont) }; + yield return new object[] { (Func)(() => SystemFonts.MessageBoxFont) }; + yield return new object[] { (Func)(() => SystemFonts.SmallCaptionFont) }; + yield return new object[] { (Func)(() => SystemFonts.StatusFont) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SystemFonts_TestData))] + public void SystemFont_Get_ReturnsExpected(Func getFont) + { + using (Font font = getFont()) + using (Font otherFont = getFont()) + { + Assert.NotNull(font); + Assert.NotNull(otherFont); + Assert.NotSame(font, otherFont); + + // Assert.Equal on a font will use the native handle to assert equality, which is not always guaranteed. + Assert.Equal(font.Name, otherFont.Name); + } + } + + public static IEnumerable SystemFonts_WindowsNames_TestData() + { + int userLangId = GetUserDefaultLCID(); + SystemFontList fonts; + + switch (userLangId & 0x3ff) + { + case 0x11: // ja-JP (Japanese) + fonts = new SystemFontList("Yu Gothic UI"); + break; + case 0x5C: // chr-Cher-US (Cherokee) + fonts = new SystemFontList("Gadugi"); + break; + case 0x12: // ko-KR (Korean) + fonts = new SystemFontList("\ub9d1\uc740\x20\uace0\ub515"); + break; + case 0x4: // zh-TW (Traditional Chinese, Taiwan) or zh-CN (Simplified Chinese, PRC) + // Although the primary language ID is the same, the fonts are different + // So we have to determine by the full language ID + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f + // Assuming this doc is correct AND the font only differs by whether it's traditional or not it should work + switch (userLangId & 0xFFFF) + { + case 0x0004: // zh-Hans + case 0x7804: // zh + case 0x0804: // zh-CN + case 0x1004: // zh-SG + fonts = new SystemFontList("Microsoft JhengHei UI"); + break; + case 0x7C04: // zh-Hant + case 0x0C04: // zh-HK + case 0x1404: // zh-MO + case 0x0404: // zh-TW + fonts = new SystemFontList("Microsoft YaHei UI"); + break; + default: + throw new InvalidOperationException("The primary language ID is Chinese, however it was not able to" + + $" determine the user locale from the LCID with value: {userLangId & 0xFFFF:X4}."); + } + break; + case 0x1E: // th-TH + case 0x54: // lo-LA + case 0x53: // km-KH + fonts = new SystemFontList("Leelawadee UI"); + break; + case 0x4A: // te-IN + case 0x49: // ta-IN + case 0x5B: // si-LK + case 0x48: // or-IN + case 0x4E: // mr-IN + case 0x4C: // ml-IN + case 0x57: // kok-IN + case 0x45: // bn-BD + case 0x4D: // as-IN + fonts = new SystemFontList("Nirmala UI"); + break; + case 0x5E: // am-ET + fonts = new SystemFontList("Ebrima"); + break; + default: // For now we assume everything else uses Segoe UI + // If there's other failure reported we can add it + fonts = new SystemFontList("Segoe UI"); + break; + } + + return fonts.ToTestData(); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SystemFonts_WindowsNames_TestData))] + public void SystemFont_Get_ReturnsExpected_WindowsNames(Func getFont, string systemFontName, string windowsFontName) + { + using (Font font = getFont()) + using (Font otherFont = getFont()) + using (Font fontFromName = SystemFonts.GetFontByName(systemFontName)) + { + Assert.NotSame(font, otherFont); + Assert.Equal(font, otherFont); + Assert.Equal(font, fontFromName); + + Assert.Equal(systemFontName, font.SystemFontName); + + // Windows 8 updated some system fonts. + if (!PlatformDetection.IsWindows7) + { + Assert.Equal(windowsFontName, font.Name); + } + } + } + + [Theory] + [InlineData(null)] + [InlineData("")] + [InlineData("captionfont")] + public void GetFontByName_NoSuchName_ReturnsNull(string systemFontName) + { + Assert.Null(SystemFonts.GetFontByName(systemFontName)); + } + + [DllImport("kernel32.dll", SetLastError = false, CharSet = CharSet.Auto)] + internal static extern int GetUserDefaultLCID(); + + // Do not test DefaultFont and DialogFont, as we can't reliably determine from LCID + // https://github.com/dotnet/runtime/issues/28830#issuecomment-473556522 + class SystemFontList + { + public SystemFontList(string c_it_m_mb_scFonts) + { + CaptionFont = c_it_m_mb_scFonts; + IconTitleFont = c_it_m_mb_scFonts; + MenuFont = c_it_m_mb_scFonts; + MessageBoxFont = c_it_m_mb_scFonts; + SmallCaptionFont = c_it_m_mb_scFonts; + StatusFont = c_it_m_mb_scFonts; + } + + public string CaptionFont { get; set; } + public string IconTitleFont { get; set; } + public string MenuFont { get; set; } + public string MessageBoxFont { get; set; } + public string SmallCaptionFont { get; set; } + public string StatusFont { get; set; } + + public IEnumerable ToTestData() + { + return new [] + { + new object[] {(Func)(() => SystemFonts.CaptionFont), nameof(CaptionFont), CaptionFont}, + new object[] {(Func)(() => SystemFonts.IconTitleFont), nameof(IconTitleFont), IconTitleFont}, + new object[] {(Func)(() => SystemFonts.MenuFont), nameof(MenuFont), MenuFont}, + new object[] {(Func)(() => SystemFonts.MessageBoxFont), nameof(MessageBoxFont), MessageBoxFont}, + new object[] {(Func)(() => SystemFonts.SmallCaptionFont), nameof(SmallCaptionFont), SmallCaptionFont}, + new object[] {(Func)(() => SystemFonts.StatusFont), nameof(StatusFont), StatusFont} + }; + } + } + } +} diff --git a/src/System.Drawing.Common/tests/SystemIconsTests.cs b/src/System.Drawing.Common/tests/SystemIconsTests.cs new file mode 100644 index 00000000000..93a119d3522 --- /dev/null +++ b/src/System.Drawing.Common/tests/SystemIconsTests.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.Drawing.Tests +{ + public class SystemIconsTests + { + public static IEnumerable SystemIcons_TestData() + { + yield return Icon(() => SystemIcons.Application); + yield return Icon(() => SystemIcons.Asterisk); + yield return Icon(() => SystemIcons.Error); + yield return Icon(() => SystemIcons.Exclamation); + yield return Icon(() => SystemIcons.Hand); + yield return Icon(() => SystemIcons.Information); + yield return Icon(() => SystemIcons.Question); + yield return Icon(() => SystemIcons.Shield); + yield return Icon(() => SystemIcons.Warning); + yield return Icon(() => SystemIcons.WinLogo); + } + + public static object[] Icon(Func getIcon) => new object[] { getIcon }; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SystemIcons_TestData))] + public void SystemIcons_Get_ReturnsExpected(Func getIcon) + { + Icon icon = getIcon(); + Assert.Same(icon, getIcon()); + } + } +} diff --git a/src/System.Drawing.Common/tests/SystemPensTest.cs b/src/System.Drawing.Common/tests/SystemPensTest.cs new file mode 100644 index 00000000000..225800bd167 --- /dev/null +++ b/src/System.Drawing.Common/tests/SystemPensTest.cs @@ -0,0 +1,91 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Drawing2D; +using System.Reflection; +using Xunit; + +namespace System.Drawing.Tests +{ + public class SystemPensTests + { + public static IEnumerable SystemPens_TestData() + { + yield return Pen(() => SystemPens.ActiveBorder, SystemColors.ActiveBorder); + yield return Pen(() => SystemPens.ActiveCaption, SystemColors.ActiveCaption); + yield return Pen(() => SystemPens.ActiveCaptionText, SystemColors.ActiveCaptionText); + yield return Pen(() => SystemPens.AppWorkspace, SystemColors.AppWorkspace); + yield return Pen(() => SystemPens.ButtonFace, SystemColors.ButtonFace); + yield return Pen(() => SystemPens.ButtonHighlight, SystemColors.ButtonHighlight); + yield return Pen(() => SystemPens.ButtonShadow, SystemColors.ButtonShadow); + yield return Pen(() => SystemPens.Control, SystemColors.Control); + yield return Pen(() => SystemPens.ControlDark, SystemColors.ControlDark); + yield return Pen(() => SystemPens.ControlDarkDark, SystemColors.ControlDarkDark); + yield return Pen(() => SystemPens.ControlLight, SystemColors.ControlLight); + yield return Pen(() => SystemPens.ControlLightLight, SystemColors.ControlLightLight); + yield return Pen(() => SystemPens.ControlText, SystemColors.ControlText); + yield return Pen(() => SystemPens.Desktop, SystemColors.Desktop); + yield return Pen(() => SystemPens.GradientActiveCaption, SystemColors.GradientActiveCaption); + yield return Pen(() => SystemPens.GradientInactiveCaption, SystemColors.GradientInactiveCaption); + yield return Pen(() => SystemPens.GrayText, SystemColors.GrayText); + yield return Pen(() => SystemPens.Highlight, SystemColors.Highlight); + yield return Pen(() => SystemPens.HighlightText, SystemColors.HighlightText); + yield return Pen(() => SystemPens.HotTrack, SystemColors.HotTrack); + yield return Pen(() => SystemPens.InactiveBorder, SystemColors.InactiveBorder); + yield return Pen(() => SystemPens.InactiveCaption, SystemColors.InactiveCaption); + yield return Pen(() => SystemPens.InactiveCaptionText, SystemColors.InactiveCaptionText); + yield return Pen(() => SystemPens.Info, SystemColors.Info); + yield return Pen(() => SystemPens.InfoText, SystemColors.InfoText); + yield return Pen(() => SystemPens.Menu, SystemColors.Menu); + yield return Pen(() => SystemPens.MenuBar, SystemColors.MenuBar); + yield return Pen(() => SystemPens.MenuHighlight, SystemColors.MenuHighlight); + yield return Pen(() => SystemPens.MenuText, SystemColors.MenuText); + yield return Pen(() => SystemPens.ScrollBar, SystemColors.ScrollBar); + yield return Pen(() => SystemPens.Window, SystemColors.Window); + yield return Pen(() => SystemPens.WindowFrame, SystemColors.WindowFrame); + yield return Pen(() => SystemPens.WindowText, SystemColors.WindowText); + } + + public static object[] Pen(Func getPen, Color expectedColor) => new object[] { getPen, expectedColor }; + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(SystemPens_TestData))] + public void SystemPens_Get_ReturnsExpected(Func getPen, Color expectedColor) + { + Pen pen = getPen(); + Assert.Equal(expectedColor, pen.Color); + Assert.Equal(PenType.SolidColor, pen.PenType); + Assert.Same(pen, getPen()); + + AssertExtensions.Throws(null, () => pen.Dispose()); + AssertExtensions.Throws(null, () => pen.SetLineCap(LineCap.ArrowAnchor, LineCap.Custom, DashCap.Round)); + + AssertExtensions.Throws(null, () => pen.Alignment = PenAlignment.Center); + AssertExtensions.Throws(null, () => pen.Brush = null); + AssertExtensions.Throws(null, () => pen.Color = Color.AliceBlue); + AssertExtensions.Throws(null, () => pen.CompoundArray = null); + AssertExtensions.Throws(null, () => pen.CustomEndCap = null); + AssertExtensions.Throws(null, () => pen.CustomStartCap = null); + AssertExtensions.Throws(null, () => pen.DashCap = DashCap.Flat); + AssertExtensions.Throws(null, () => pen.DashStyle = DashStyle.Custom); + AssertExtensions.Throws(null, () => pen.DashOffset = 10); + AssertExtensions.Throws(null, () => pen.DashPattern = null); + AssertExtensions.Throws(null, () => pen.EndCap = LineCap.RoundAnchor); + AssertExtensions.Throws(null, () => pen.LineJoin = LineJoin.MiterClipped); + AssertExtensions.Throws(null, () => pen.MiterLimit = 10); + AssertExtensions.Throws(null, () => pen.StartCap = LineCap.RoundAnchor); + using (var matrix = new Matrix()) + { + AssertExtensions.Throws(null, () => pen.Transform = matrix); + } + AssertExtensions.Throws(null, () => pen.Width = 10); + } + + [Fact] + public void FromSystemColor_NotSystemColor_ThrowsArgumentException() + { + AssertExtensions.Throws(null, () => SystemPens.FromSystemColor(Color.Blue)); + } + } +} diff --git a/src/System.Drawing.Common/tests/Text/InstalledFontCollectionTests.cs b/src/System.Drawing.Common/tests/Text/InstalledFontCollectionTests.cs new file mode 100644 index 00000000000..d95204c45c9 --- /dev/null +++ b/src/System.Drawing.Common/tests/Text/InstalledFontCollectionTests.cs @@ -0,0 +1,37 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace System.Drawing.Text.Tests +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported))] + public class InstalledFontCollectionTests + { + [Fact] + public void Ctor_Default() + { + using (var fontCollection = new InstalledFontCollection()) + { + Assert.NotEmpty(fontCollection.Families); + } + } + + [Fact] + public void Families_GetWhenDisposed_ReturnsNonEmpty() + { + var fontCollection = new InstalledFontCollection(); + fontCollection.Dispose(); + + Assert.NotEmpty(fontCollection.Families); + } + + [Fact] + public void Dispose_MultipleTimes_Nop() + { + var fontCollection = new InstalledFontCollection(); + fontCollection.Dispose(); + fontCollection.Dispose(); + } + } +} diff --git a/src/System.Drawing.Common/tests/Text/PrivateFontCollectionTests.cs b/src/System.Drawing.Common/tests/Text/PrivateFontCollectionTests.cs new file mode 100644 index 00000000000..df88f339ab3 --- /dev/null +++ b/src/System.Drawing.Common/tests/Text/PrivateFontCollectionTests.cs @@ -0,0 +1,254 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Drawing.Tests; +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace System.Drawing.Text.Tests +{ + public class PrivateFontCollectionTests + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_Default() + { + using (var fontCollection = new PrivateFontCollection()) + { + Assert.Empty(fontCollection.Families); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_AbsolutePath_Success() + { + // GDI+ on Windows 7 incorrectly throws a FileNotFoundException. + if (PlatformDetection.IsWindows7) + { + return; + } + + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestBitmapPath("empty.file")); + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + FontFamily fontFamily = Assert.Single(fontCollection.Families); + Assert.Equal("Code New Roman", fontFamily.Name); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_RelativePath_Success() + { + // GDI+ on Windows 7 incorrectly throws a FileNotFoundException. + if (PlatformDetection.IsWindows7) + { + return; + } + + using (var fontCollection = new PrivateFontCollection()) + { + string relativePath = Path.Combine("fonts", "CodeNewRoman.ttf"); + fontCollection.AddFontFile(relativePath); + + FontFamily fontFamily = Assert.Single(fontCollection.Families); + Assert.Equal("Code New Roman", fontFamily.Name); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_SamePathMultipleTimes_FamiliesContainsOnlyOneFont() + { + // GDI+ on Windows 7 incorrectly throws a FileNotFoundException. + if (PlatformDetection.IsWindows7) + { + return; + } + + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.ttf")); + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.ttf")); + + FontFamily fontFamily = Assert.Single(fontCollection.Families); + Assert.Equal("Code New Roman", fontFamily.Name); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_SameNameMultipleTimes_FamiliesContainsFirstFontOnly() + { + // GDI+ on Windows 7 incorrectly throws a FileNotFoundException. + if (PlatformDetection.IsWindows7) + { + return; + } + + using (var fontCollection = new PrivateFontCollection()) + { + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.ttf")); + fontCollection.AddFontFile(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + // Verify that the first file is used by checking that it contains metadata + // associated with CodeNewRoman.ttf. + const int FrenchLCID = 1036; + FontFamily fontFamily = Assert.Single(fontCollection.Families); + Assert.Equal("Code New Roman", fontFamily.Name); + Assert.Equal("Bonjour", fontFamily.GetName(FrenchLCID)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_NullFileName_ThrowsArgumentNullException() + { + using (var fontCollection = new PrivateFontCollection()) + { + AssertExtensions.Throws("filename", "path", () => fontCollection.AddFontFile(null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_InvalidPath_ThrowsArgumentException() + { + using (var fontCollection = new PrivateFontCollection()) + { + AssertExtensions.Throws("path", null, () => fontCollection.AddFontFile(string.Empty)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_NoSuchFilePath_ThrowsFileNotFoundException() + { + using (var fontCollection = new PrivateFontCollection()) + { + Assert.Throws(() => fontCollection.AddFontFile("fileName")); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/17328")] + public void AddFontFile_LongFilePath_ThrowsException() + { + using (var fontCollection = new PrivateFontCollection()) + { + // Throws PathTooLongException on Desktop and FileNotFoundException elsewhere. + if (PlatformDetection.IsNetFramework) + { + Assert.Throws( + () => fontCollection.AddFontFile(new string('a', 261))); + } + else + { + Assert.Throws( + () => fontCollection.AddFontFile(new string('a', 261))); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_Directory_ThrowsFileNotFoundException() + { + using (var fontCollection = new PrivateFontCollection()) + { + AssertExtensions.Throws(() => fontCollection.AddFontFile(AppContext.BaseDirectory)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddFontFile_Disposed_ThrowsArgumentException() + { + var fontCollection = new PrivateFontCollection(); + fontCollection.Dispose(); + + AssertExtensions.Throws(null, () => fontCollection.AddFontFile("fileName")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddMemoryFont_ValidMemory_Success() + { + using (var fontCollection = new PrivateFontCollection()) + { + byte[] data = File.ReadAllBytes(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + IntPtr fontBuffer = Marshal.AllocCoTaskMem(data.Length); + try + { + Marshal.Copy(data, 0, fontBuffer, data.Length); + fontCollection.AddMemoryFont(fontBuffer, data.Length); + + FontFamily font = Assert.Single(fontCollection.Families); + Assert.Equal("Code New Roman", font.Name); + } + finally + { + Marshal.FreeCoTaskMem(fontBuffer); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddMemoryFont_ZeroMemory_ThrowsArgumentException() + { + using (var fontCollection = new PrivateFontCollection()) + { + AssertExtensions.Throws(null, () => fontCollection.AddMemoryFont(IntPtr.Zero, 100)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(0)] + [InlineData(-1)] + public void AddMemoryFont_InvalidLength_ThrowsArgumentException(int length) + { + // GDI+ on Windows 7 incorrectly throws a FileNotFoundException. + if (PlatformDetection.IsWindows) + { + return; + } + + using (var fontCollection = new PrivateFontCollection()) + { + byte[] data = File.ReadAllBytes(Helpers.GetTestFontPath("CodeNewRoman.otf")); + + IntPtr fontBuffer = Marshal.AllocCoTaskMem(data.Length); + try + { + Marshal.Copy(data, 0, fontBuffer, data.Length); + AssertExtensions.Throws(null, () => fontCollection.AddMemoryFont(fontBuffer, length)); + } + finally + { + Marshal.FreeCoTaskMem(fontBuffer); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void AddMemoryFont_Disposed_ThrowsArgumentException() + { + var fontCollection = new PrivateFontCollection(); + fontCollection.Dispose(); + + AssertExtensions.Throws(null, () => fontCollection.AddMemoryFont((IntPtr)10, 100)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Families_GetWhenDisposed_ThrowsArgumentException() + { + var fontCollection = new PrivateFontCollection(); + fontCollection.Dispose(); + + AssertExtensions.Throws(null, () => fontCollection.Families); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Dispose_MultipleTimes_Nop() + { + var fontCollection = new PrivateFontCollection(); + fontCollection.Dispose(); + fontCollection.Dispose(); + } + } +} diff --git a/src/System.Drawing.Common/tests/TextureBrushTests.cs b/src/System.Drawing.Common/tests/TextureBrushTests.cs new file mode 100644 index 00000000000..a7159c2271e --- /dev/null +++ b/src/System.Drawing.Common/tests/TextureBrushTests.cs @@ -0,0 +1,902 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using Xunit; + +namespace System.Drawing.Tests +{ + public class TextureBrushTests + { + public static IEnumerable Ctor_Bitmap_TestData() + { + yield return new object[] { new Bitmap(10, 10), PixelFormat.Format32bppPArgb, new Size(10, 10) }; + yield return new object[] { new Metafile(Helpers.GetTestBitmapPath("telescope_01.wmf")), PixelFormat.Format32bppArgb, new Size(490, 654) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Bitmap_TestData))] + public void Ctor_Bitmap(Image bitmap, PixelFormat expectedPixelFormat, Size expectedSize) + { + try + { + using (var brush = new TextureBrush(bitmap)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(bitmap, brushImage); + Assert.Equal(expectedPixelFormat, brushImage.PixelFormat); + Assert.Equal(expectedSize, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + } + } + finally + { + bitmap.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_BitmapFromIconHandle_Success() + { + using (var icon = new Icon(Helpers.GetTestBitmapPath("10x16_one_entry_32bit.ico"))) + using (var image = Bitmap.FromHicon(icon.Handle)) + { + Ctor_Bitmap(image, PixelFormat.Format32bppPArgb, new Size(11, 22)); + } + } + + public static IEnumerable Ctor_Image_WrapMode_TestData() + { + foreach (object[] data in Ctor_Bitmap_TestData()) + { + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.Clamp, data[1], data[2] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.Tile, data[1], data[2] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipX, data[1], data[2] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipXY, data[1], data[2] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipY, data[1], data[2] }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_WrapMode_TestData))] + public void Ctor_Image_WrapMode(Image image, WrapMode wrapMode, PixelFormat expectedPixelFormat, Size expectedSize) + { + try + { + using (var brush = new TextureBrush(image, wrapMode)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(expectedPixelFormat, brushImage.PixelFormat); + Assert.Equal(expectedSize, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(wrapMode, brush.WrapMode); + } + } + finally + { + image.Dispose(); + } + } + + public static IEnumerable Ctor_Image_Rectangle_TestData() + { + yield return new object[] { new Bitmap(10, 10), new Rectangle(0, 0, 10, 10) }; + yield return new object[] { new Bitmap(10, 10), new Rectangle(5, 5, 5, 5) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_Rectangle_TestData))] + public void Ctor_Image_Rectangle(Image image, Rectangle rectangle) + { + try + { + using (var brush = new TextureBrush(image, rectangle)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + } + } + finally + { + image.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_Rectangle_TestData))] + public void Ctor_Image_RectangleF(Image image, Rectangle rectangle) + { + try + { + using (var brush = new TextureBrush(image, (RectangleF)rectangle)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(WrapMode.Tile, brush.WrapMode); + } + } + finally + { + image.Dispose(); + } + } + + public static IEnumerable Ctor_Image_WrapMode_Rectangle_TestData() + { + foreach (object[] data in Ctor_Image_Rectangle_TestData()) + { + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.Clamp, data[1] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.Tile, data[1] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipX, data[1] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipXY, data[1] }; + yield return new object[] { ((Image)data[0]).Clone(), WrapMode.TileFlipY, data[1] }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_WrapMode_Rectangle_TestData))] + public void Ctor_Image_WrapMode_Rectangle(Image image, WrapMode wrapMode, Rectangle rectangle) + { + try + { + using (var brush = new TextureBrush(image, wrapMode, rectangle)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(wrapMode, brush.WrapMode); + } + } + finally + { + image.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_WrapMode_Rectangle_TestData))] + public void Ctor_Image_WrapMode_RectangleF(Image image, WrapMode wrapMode, Rectangle rectangle) + { + try + { + using (var brush = new TextureBrush(image, wrapMode, (RectangleF)rectangle)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(wrapMode, brush.WrapMode); + } + } + finally + { + image.Dispose(); + } + } + + public static IEnumerable Ctor_Image_Rectangle_ImageAttributes_TestData() + { + foreach (object[] data in Ctor_Image_Rectangle_TestData()) + { + yield return new object[] { ((Image)data[0]).Clone(), data[1], null, WrapMode.Tile }; + yield return new object[] { ((Image)data[0]).Clone(), data[1], new ImageAttributes(), WrapMode.Clamp }; + + var customWrapMode = new ImageAttributes(); + customWrapMode.SetWrapMode(WrapMode.TileFlipXY); + yield return new object[] { ((Image)data[0]).Clone(), data[1], customWrapMode, WrapMode.TileFlipXY }; + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_Rectangle_ImageAttributes_TestData))] + public void Ctor_Image_Rectangle_ImageAttributes(Image image, Rectangle rectangle, ImageAttributes attributes, WrapMode expectedWrapMode) + { + try + { + using (var brush = new TextureBrush(image, rectangle, attributes)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(expectedWrapMode, brush.WrapMode); + } + } + finally + { + image.Dispose(); + attributes?.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_Image_Rectangle_ImageAttributes_TestData))] + public void Ctor_Image_RectangleF_ImageAttributes(Image image, Rectangle rectangle, ImageAttributes attributes, WrapMode expectedWrapMode) + { + try + { + using (var brush = new TextureBrush(image, (RectangleF)rectangle, attributes)) + using (var matrix = new Matrix()) + { + Bitmap brushImage = Assert.IsType(brush.Image); + Assert.NotSame(image, brushImage); + Assert.Equal(PixelFormat.Format32bppPArgb, brushImage.PixelFormat); + Assert.Equal(rectangle.Size, brushImage.Size); + Assert.Equal(matrix, brush.Transform); + Assert.Equal(expectedWrapMode, brush.WrapMode); + } + } + finally + { + image.Dispose(); + attributes?.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_NullImage_ThrowsArgumentNullException() + { + AssertExtensions.Throws("image", () => new TextureBrush(null)); + AssertExtensions.Throws("image", () => new TextureBrush(null, WrapMode.Tile)); + AssertExtensions.Throws("image", () => new TextureBrush(null, RectangleF.Empty)); + AssertExtensions.Throws("image", () => new TextureBrush(null, Rectangle.Empty)); + AssertExtensions.Throws("image", () => new TextureBrush(null, RectangleF.Empty, null)); + AssertExtensions.Throws("image", () => new TextureBrush(null, Rectangle.Empty, null)); + AssertExtensions.Throws("image", () => new TextureBrush(null, WrapMode.Tile, RectangleF.Empty)); + AssertExtensions.Throws("image", () => new TextureBrush(null, WrapMode.Tile, Rectangle.Empty)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Ctor_DisposedImage_ThrowsArgumentException() + { + var image = new Bitmap(10, 10); + image.Dispose(); + + AssertExtensions.Throws(null, () => new TextureBrush(image)); + AssertExtensions.Throws(null, () => new TextureBrush(image, WrapMode.Tile)); + AssertExtensions.Throws(null, () => new TextureBrush(image, RectangleF.Empty)); + AssertExtensions.Throws(null, () => new TextureBrush(image, Rectangle.Empty)); + AssertExtensions.Throws(null, () => new TextureBrush(image, RectangleF.Empty, null)); + AssertExtensions.Throws(null, () => new TextureBrush(image, Rectangle.Empty, null)); + AssertExtensions.Throws(null, () => new TextureBrush(image, WrapMode.Tile, RectangleF.Empty)); + AssertExtensions.Throws(null, () => new TextureBrush(image, WrapMode.Tile, Rectangle.Empty)); + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WrapMode.Tile - 1)] + [InlineData(WrapMode.Clamp + 1)] + public void Ctor_InvalidWrapMode_ThrowsInvalidEnumArgumentException(WrapMode wrapMode) + { + using (var image = new Bitmap(10, 10)) + { + Assert.ThrowsAny(() => new TextureBrush(image, wrapMode)); + Assert.ThrowsAny(() => new TextureBrush(image, wrapMode, RectangleF.Empty)); + Assert.ThrowsAny(() => new TextureBrush(image, wrapMode, Rectangle.Empty)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(-1, 0, 1, 1)] + [InlineData(10, 0, 1, 1)] + [InlineData(5, 0, 6, 1)] + [InlineData(0, -1, 1, 1)] + [InlineData(0, 10, 1, 1)] + [InlineData(0, 5, 1, 6)] + [InlineData(0, 0, 1, 0)] + [InlineData(0, 0, 0, 1)] + public void Ctor_InvalidRectangle_ThrowsOutOfMemoryException(int x, int y, int width, int height) + { + var rectangle = new Rectangle(x, y, width, height); + using (var image = new Bitmap(10, 10)) + { + Assert.Throws(() => new TextureBrush(image, rectangle)); + Assert.Throws(() => new TextureBrush(image, (RectangleF)rectangle)); + Assert.Throws(() => new TextureBrush(image, WrapMode.Tile, rectangle)); + Assert.Throws(() => new TextureBrush(image, WrapMode.Tile, (RectangleF)rectangle)); + Assert.Throws(() => new TextureBrush(image, rectangle, null)); + Assert.Throws(() => new TextureBrush(image, (RectangleF)rectangle, null)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Invoke_Success() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image, WrapMode.Clamp)) + { + TextureBrush clone = Assert.IsType(brush.Clone()); + Assert.NotSame(brush, clone); + + Assert.Equal(new Size(10, 10), brush.Image.Size); + Assert.Equal(WrapMode.Clamp, clone.WrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Clone()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Image_GetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Image); + } + } + + public static IEnumerable MultiplyTransform_TestData() + { + yield return new object[] { new Matrix(), new Matrix(1, 2, 3, 4, 5, 6), MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), new Matrix(1, 2, 3, 4, 5, 6), MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), new Matrix(2, 3, 4, 5, 6, 7), MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), new Matrix(2, 3, 4, 5, 6, 7), MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(MultiplyTransform_TestData))] + public void MultiplyTransform_Matrix_SetsTransformToExpected(Matrix originalTransform, Matrix matrix, MatrixOrder matrixOrder) + { + try + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var expected = (Matrix)originalTransform.Clone()) + { + expected.Multiply(matrix, matrixOrder); + brush.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + TextureBrush clone = (TextureBrush)brush.Clone(); + clone.MultiplyTransform(matrix); + Assert.Equal(expected, clone.Transform); + } + + brush.MultiplyTransform(matrix, matrixOrder); + Assert.Equal(expected, brush.Transform); + } + } + finally + { + originalTransform.Dispose(); + matrix.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NullMatrix_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null)); + AssertExtensions.Throws("matrix", () => brush.MultiplyTransform(null, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_NotInvertibleMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var matrix = new Matrix(123, 24, 82, 16, 47, 30)) + { + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_DisposedMatrix_Nop() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + { + brush.Transform = transform; + + var matrix = new Matrix(); + matrix.Dispose(); + + brush.MultiplyTransform(matrix); + brush.MultiplyTransform(matrix, MatrixOrder.Append); + + Assert.Equal(transform, brush.Transform); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void MultiplyTransform_InvalidOrder_Nop(MatrixOrder matrixOrder) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix()) + { + brush.Transform = transform; + + brush.MultiplyTransform(matrix, matrixOrder); + Assert.Equal(transform, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void MultiplyTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix)); + AssertExtensions.Throws(null, () => brush.MultiplyTransform(matrix, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Invoke_SetsTransformToZero() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var transform = new Matrix(1, 2, 3, 4, 5, 6)) + using (var matrix = new Matrix()) + { + brush.Transform = transform; + brush.ResetTransform(); + Assert.Equal(matrix, brush.Transform); + + brush.ResetTransform(); + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ResetTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ResetTransform()); + } + } + + public static IEnumerable RotateTransform_TestData() + { + yield return new object[] { new Matrix(), 90, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 90, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 360, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 360, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -45, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -45, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(RotateTransform_TestData))] + public void RotateTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float angle, MatrixOrder matrixOrder) + { + try + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Rotate(angle, matrixOrder); + brush.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + TextureBrush clone = (TextureBrush)brush.Clone(); + clone.RotateTransform(angle); + Assert.Equal(expected, clone.Transform); + } + + brush.RotateTransform(angle, matrixOrder); + Assert.Equal(expected, brush.Transform); + } + } + finally + { + originalTransform.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void RotateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + AssertExtensions.Throws(null, () => brush.RotateTransform(10, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void RotateTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.RotateTransform(1)); + AssertExtensions.Throws(null, () => brush.RotateTransform(1, MatrixOrder.Prepend)); + } + } + + public static IEnumerable ScaleTransform_TestData() + { + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(ScaleTransform_TestData))] + public void ScaleTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float scaleX, float scaleY, MatrixOrder matrixOrder) + { + try + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Scale(scaleX, scaleY, matrixOrder); + brush.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + TextureBrush clone = (TextureBrush)brush.Clone(); + clone.ScaleTransform(scaleX, scaleY); + Assert.Equal(expected, clone.Transform); + } + + brush.ScaleTransform(scaleX, scaleY, matrixOrder); + Assert.Equal(expected, brush.Transform); + } + } + finally + { + originalTransform.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void ScaleTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + AssertExtensions.Throws(null, () => brush.ScaleTransform(1, 2, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void ScaleTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.ScaleTransform(1, 2)); + AssertExtensions.Throws(null, () => brush.ScaleTransform(1, 2, MatrixOrder.Prepend)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetValid_GetReturnsExpected() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (var matrix = new Matrix(1, 2, 3, 4, 5, 6)) + { + brush.Transform = matrix; + Assert.Equal(matrix, brush.Transform); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetNull_ThrowsArgumentNullException() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + AssertExtensions.Throws("value", () => brush.Transform = null); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_SetDisposedMatrix_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + var matrix = new Matrix(); + matrix.Dispose(); + + AssertExtensions.Throws(null, () => brush.Transform = matrix); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Transform_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.Transform); + AssertExtensions.Throws(null, () => brush.Transform = matrix); + } + } + + public static IEnumerable TranslateTransform_TestData() + { + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(), 2, 3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0, 0, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 1, 1, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), -2, -3, MatrixOrder.Append }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Prepend }; + yield return new object[] { new Matrix(1, 2, 3, 4, 5, 6), 0.5, 0.75, MatrixOrder.Append }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(TranslateTransform_TestData))] + public void TranslateTransform_Invoke_SetsTransformToExpected(Matrix originalTransform, float dX, float dY, MatrixOrder matrixOrder) + { + try + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + using (Matrix expected = originalTransform.Clone()) + { + expected.Translate(dX, dY, matrixOrder); + brush.Transform = originalTransform; + + if (matrixOrder == MatrixOrder.Prepend) + { + TextureBrush clone = (TextureBrush)brush.Clone(); + clone.TranslateTransform(dX, dY); + Assert.Equal(expected, clone.Transform); + } + + brush.TranslateTransform(dX, dY, matrixOrder); + Assert.Equal(expected, brush.Transform); + } + } + finally + { + originalTransform.Dispose(); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(MatrixOrder.Prepend - 1)] + [InlineData(MatrixOrder.Append + 1)] + public void TranslateTransform_InvalidOrder_ThrowsArgumentException(MatrixOrder matrixOrder) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + AssertExtensions.Throws(null, () => brush.TranslateTransform(1, 2, matrixOrder)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TranslateTransform_Disposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + using (var matrix = new Matrix()) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.TranslateTransform(1, 2)); + AssertExtensions.Throws(null, () => brush.TranslateTransform(1, 2, MatrixOrder.Prepend)); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WrapMode.Clamp)] + [InlineData(WrapMode.Tile)] + [InlineData(WrapMode.TileFlipX)] + [InlineData(WrapMode.TileFlipXY)] + [InlineData(WrapMode.TileFlipY)] + public void WrapMode_SetValid_GetReturnsExpected(WrapMode wrapMode) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + brush.WrapMode = wrapMode; + Assert.Equal(wrapMode, brush.WrapMode); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(WrapMode.Tile - 1)] + [InlineData(WrapMode.Clamp + 1)] + public void WrapMode_SetInvalid_ThrowsInvalidEnumArgumentException(WrapMode wrapMode) + { + using (var image = new Bitmap(10, 10)) + using (var brush = new TextureBrush(image)) + { + Assert.ThrowsAny(() => brush.WrapMode = wrapMode); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_GetSetWhenDisposed_ThrowsArgumentException() + { + using (var image = new Bitmap(10, 10)) + { + var brush = new TextureBrush(image); + brush.Dispose(); + + AssertExtensions.Throws(null, () => brush.WrapMode); + AssertExtensions.Throws(null, () => brush.WrapMode = WrapMode.Tile); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_Clamp_ReturnsExpected() + { + // R|G|_|_ + // B|Y|_|_ + // _|_|_|_ + // _|_|_|_ + Color empty = Color.FromArgb(0, 0, 0, 0); + VerifyFillRect(WrapMode.Clamp, new Color[][] + { + new Color[] { Color.Red, Color.Green, empty, empty }, + new Color[] { Color.Blue, Color.Yellow, empty, empty }, + new Color[] { empty, empty, empty, empty }, + new Color[] { empty, empty, empty, empty } + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_Tile_ReturnsExpected() + { + // R|G|R|G + // B|Y|B|Y + // R|G|R|G + // B|Y|B|Y + VerifyFillRect(WrapMode.Tile, new Color[][] + { + new Color[] { Color.Red, Color.Green, Color.Red, Color.Green }, + new Color[] { Color.Blue, Color.Yellow, Color.Blue, Color.Yellow }, + new Color[] { Color.Red, Color.Green, Color.Red, Color.Green }, + new Color[] { Color.Blue, Color.Yellow, Color.Blue, Color.Yellow } + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_TileFlipX_ReturnsExpected() + { + // R|G|G|R + // B|Y|Y|B + // R|G|G|R + // B|Y|Y|B + VerifyFillRect(WrapMode.TileFlipX, new Color[][] + { + new Color[] { Color.Red, Color.Green, Color.Green, Color.Red }, + new Color[] { Color.Blue, Color.Yellow, Color.Yellow, Color.Blue }, + new Color[] { Color.Red, Color.Green, Color.Green, Color.Red }, + new Color[] { Color.Blue, Color.Yellow, Color.Yellow, Color.Blue } + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_TileFlipY_ReturnsExpected() + { + // R|G|R|G + // B|Y|B|Y + // B|Y|B|Y + // R|G|R|G + VerifyFillRect(WrapMode.TileFlipY, new Color[][] + { + new Color[] { Color.Red, Color.Green, Color.Red, Color.Green }, + new Color[] { Color.Blue, Color.Yellow, Color.Blue, Color.Yellow }, + new Color[] { Color.Blue, Color.Yellow, Color.Blue, Color.Yellow }, + new Color[] { Color.Red, Color.Green, Color.Red, Color.Green } + }); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WrapMode_TileFlipXY_ReturnsExpected() + { + // R|G|G|R + // B|Y|Y|B + // B|Y|Y|B + // R|G|G|R + VerifyFillRect(WrapMode.TileFlipXY, new Color[][] + { + new Color[] { Color.Red, Color.Green, Color.Green, Color.Red }, + new Color[] { Color.Blue, Color.Yellow, Color.Yellow, Color.Blue }, + new Color[] { Color.Blue, Color.Yellow, Color.Yellow, Color.Blue }, + new Color[] { Color.Red, Color.Green, Color.Green, Color.Red } + }); + } + + private static void VerifyFillRect(WrapMode wrapMode, Color[][] expectedColors) + { + using (var brushBitmap = new Bitmap(2, 2)) + { + brushBitmap.SetPixel(0, 0, Color.Red); + brushBitmap.SetPixel(1, 0, Color.Green); + brushBitmap.SetPixel(0, 1, Color.Blue); + brushBitmap.SetPixel(1, 1, Color.Yellow); + + using (var brush = new TextureBrush(brushBitmap, wrapMode)) + using (var targetImage = new Bitmap(4, 4)) + using (Graphics targetGraphics = Graphics.FromImage(targetImage)) + { + targetGraphics.FillRectangle(brush, new Rectangle(0, 0, 4, 4)); + + Helpers.VerifyBitmap(targetImage, expectedColors); + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/ToolboxBitmapAttributeTests.cs b/src/System.Drawing.Common/tests/ToolboxBitmapAttributeTests.cs new file mode 100644 index 00000000000..d9526d99d98 --- /dev/null +++ b/src/System.Drawing.Common/tests/ToolboxBitmapAttributeTests.cs @@ -0,0 +1,212 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Diagnostics; +using Xunit; + +#pragma warning disable CA1050 // Declare types in namespaces +public class ClassWithNoNamespace { } +#pragma warning restore CA1050 + +namespace System.Drawing.Tests +{ + public class bitmap_173x183_indexed_8bit { } + + public class Icon_toolboxBitmapAttributeTest { } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class ToolboxBitmapAttributeTests + { + private static Size DefaultSize = new Size(16, 16); + private void AssertDefaultSize(Image image) + { + try + { + Assert.Equal(DefaultSize, image.Size); + } + catch (Exception ex) + { + // On .NET Framework sometimes the size might be default or it might + // be disposed in which case Size property throws an ArgumentException + // so allow both cases see https://github.com/dotnet/runtime/issues/25144. + if (PlatformDetection.IsNetFramework && ex is ArgumentException) + { + return; + } + throw; + } + } + + public static IEnumerable Ctor_FileName_TestData() + { + yield return new object[] { null, new Size(0, 0) }; + yield return new object[] { Helpers.GetTestBitmapPath("bitmap_173x183_indexed_8bit.bmp"), new Size(173, 183) }; + yield return new object[] { Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"), new Size(16, 16) }; + yield return new object[] { Helpers.GetTestBitmapPath("invalid.ico"), new Size(0, 0) }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Ctor_FileName_TestData))] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] + public void Ctor_FileName(string fileName, Size size) + { + var attribute = new ToolboxBitmapAttribute(fileName); + + using (Image image = attribute.GetImage(null)) + { + if (size == Size.Empty) + { + AssertDefaultSize(image); + } + else + { + Assert.Equal(size, image.Size); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(null, -1, -1)] + [InlineData(typeof(ClassWithNoNamespace), -1, -1)] + [InlineData(typeof(bitmap_173x183_indexed_8bit), 173, 183)] + [InlineData(typeof(ToolboxBitmapAttributeTests), -1, -1)] + public void Ctor_Type(Type type, int width, int height) + { + var attribute = new ToolboxBitmapAttribute(type); + using (Image image = attribute.GetImage(type)) + { + if (width == -1 && height == -1) + { + AssertDefaultSize(image); + } + else + { + Assert.Equal(new Size(width, height), image.Size); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData(null, null, -1, -1)] + [InlineData(null, "invalid.ico", -1, -1)] + [InlineData(typeof(ClassWithNoNamespace), null, -1, -1)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "", -1, -1)] + [InlineData(typeof(ToolboxBitmapAttributeTests), null, -1, -1)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "invalid.ico", -1, -1)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "48x48_multiple_entries_4bit", 16, 16)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "48x48_multiple_entries_4bit.ico", 16, 16)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "empty.file", -1, -1)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "bitmap_173x183_indexed_8bit", 173, 183)] + [InlineData(typeof(ToolboxBitmapAttributeTests), "bitmap_173x183_indexed_8bit.bmp", 173, 183)] + public void Ctor_Type_String(Type type, string fileName, int width, int height) + { + var attribute = new ToolboxBitmapAttribute(type, fileName); + + using (Image image = attribute.GetImage(type, fileName, false)) + { + if (width == -1 && height == -1) + { + AssertDefaultSize(image); + } + else + { + Assert.Equal(new Size(width, height), image.Size); + } + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [InlineData("bitmap_173x183_indexed_8bit.bmp", 173, 183)] + [InlineData("48x48_multiple_entries_4bit.ico", 16, 16)] + public void GetImage_TypeFileNameBool_ReturnsExpected(string fileName, int width, int height) + { + var attribute = new ToolboxBitmapAttribute((string)null); + using (Image image = attribute.GetImage(typeof(ToolboxBitmapAttributeTests), fileName, large: true)) + { + Assert.Equal(new Size(32, 32), image.Size); + } + + using (Image image = attribute.GetImage(typeof(ToolboxBitmapAttributeTests), fileName, large: false)) + { + Assert.Equal(new Size(width, height), image.Size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetImage_NullComponent_ReturnsNull() + { + var attribute = new ToolboxBitmapAttribute((string)null); + Assert.Null(attribute.GetImage((object)null)); + Assert.Null(attribute.GetImage((object)null, true)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetImage_Component_ReturnsExpected() + { + ToolboxBitmapAttribute attribute = new ToolboxBitmapAttribute((string)null); + + using (Image smallImage = attribute.GetImage(new bitmap_173x183_indexed_8bit(), large: false)) + { + Assert.Equal(new Size(173, 183), smallImage.Size); + + using (Image largeImage = attribute.GetImage(new bitmap_173x183_indexed_8bit(), large: true)) + { + Assert.Equal(new Size(32, 32), largeImage.Size); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetImage_Default_ReturnsExpected() + { + ToolboxBitmapAttribute attribute = ToolboxBitmapAttribute.Default; + + using (Image image = attribute.GetImage(typeof(ToolboxBitmapAttributeTests), "bitmap_173x183_indexed_8bit", large: true)) + { + Assert.Equal(new Size(32, 32), image.Size); + } + + using (Image image = attribute.GetImage(typeof(ToolboxBitmapAttributeTests), "bitmap_173x183_indexed_8bit", large: false)) + { + Assert.Equal(new Size(173, 183), image.Size); + } + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Logical name with no extension is not supported in .NET Framework")] + [InlineData(typeof(Icon_toolboxBitmapAttributeTest), 256, 256)] + public void GetImage_NoExtension(Type type, int width, int height) + { + var attribute = new ToolboxBitmapAttribute(type); + using (Image image = attribute.GetImage(type)) + { + if (width == -1 && height == -1) + { + AssertDefaultSize(image); + } + else + { + Assert.Equal(new Size(width, height), image.Size); + } + } + } + + public static IEnumerable Equals_TestData() + { + yield return new object[] { ToolboxBitmapAttribute.Default, ToolboxBitmapAttribute.Default, true }; + yield return new object[] { ToolboxBitmapAttribute.Default, new ToolboxBitmapAttribute(typeof(ToolboxBitmapAttribute), "bitmap_173x183_indexed_8bit"), true }; + + yield return new object[] { ToolboxBitmapAttribute.Default, new object(), false }; + yield return new object[] { ToolboxBitmapAttribute.Default, null, false }; + } + + [ConditionalTheory(Helpers.IsDrawingSupported)] + [MemberData(nameof(Equals_TestData))] + public void Equals_Other_ReturnsExpected(ToolboxBitmapAttribute attribute, object other, bool expected) + { + Assert.Equal(expected, attribute.Equals(other)); + Assert.Equal(attribute.GetHashCode(), attribute.GetHashCode()); + } + } +} diff --git a/src/System.Drawing.Common/tests/TrimmingTests/IconSave.cs b/src/System.Drawing.Common/tests/TrimmingTests/IconSave.cs new file mode 100644 index 00000000000..0e15a645c51 --- /dev/null +++ b/src/System.Drawing.Common/tests/TrimmingTests/IconSave.cs @@ -0,0 +1,28 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; +using System.IO; + +/// +/// Tests that Icon.Save works when the Icon is loaded from an IntPtr. +/// This causes COM to be used to save the Icon. +/// +class Program +{ + static int Main(string[] args) + { + Icon i = SystemIcons.WinLogo; + using MemoryStream stream = new(); + + i.Save(stream); + + // ensure something was written to the stream + if (stream.Position == 0) + { + return -1; + } + + return 100; + } +} diff --git a/src/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj b/src/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj new file mode 100644 index 00000000000..71883b846cb --- /dev/null +++ b/src/System.Drawing.Common/tests/TrimmingTests/System.Drawing.Common.TrimmingTests.proj @@ -0,0 +1,15 @@ + + + + + System.Drawing.Common + + + + + + + + diff --git a/src/System.Drawing.Common/tests/default.rd.xml b/src/System.Drawing.Common/tests/default.rd.xml new file mode 100644 index 00000000000..c2aeeadaf8e --- /dev/null +++ b/src/System.Drawing.Common/tests/default.rd.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/BmpCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/BmpCodecTests.cs new file mode 100644 index 00000000000..7c3eefd078e --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/BmpCodecTests.cs @@ -0,0 +1,540 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// BMPCodec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// (C) 2004 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + public class BmpCodecTest + { + /* Checks bitmap features on a known 1bbp bitmap */ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap1bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver1bit.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format4bppIndexed, bmp.PixelFormat); + Assert.Equal(173, bmp.Width); + Assert.Equal(183, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(173, rect.Width); + Assert.Equal(183, rect.Height); + + Assert.Equal(173, bmp.Size.Width); + Assert.Equal(183, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap1bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver1bit.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-1, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-8355840, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-8355840, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(160, 160).ToArgb()); + } + } + + /* Checks bitmap features on a known 8bbp bitmap */ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver8bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format8bppIndexed, bmp.PixelFormat); + Assert.Equal(173, bmp.Width); + Assert.Equal(183, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(173, rect.Width); + Assert.Equal(183, rect.Height); + + Assert.Equal(173, bmp.Size.Width); + Assert.Equal(183, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver8bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-1040, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-6250304, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-12566464, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-6258560, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-8355776, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-12566464, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-2039680, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-4153280, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-12566464, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-6258560, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-12566464, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-1040, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-4144960, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-4137792, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-8355712, bmp.GetPixel(160, 160).ToArgb()); + } + } + + /* Checks bitmap features on a known 24-bits bitmap */ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap24bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format24bppRgb, bmp.PixelFormat); + Assert.Equal(173, bmp.Width); + Assert.Equal(183, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(173, rect.Width); + Assert.Equal(183, rect.Height); + + Assert.Equal(173, bmp.Size.Width); + Assert.Equal(183, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap24bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-1645353, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-461332, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-330005, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-2237489, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-1251105, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-3024947, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-2699070, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-2366734, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-4538413, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-6116681, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-7369076, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-13024729, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-7174020, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-51, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16053503, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-8224431, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16579326, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-2502457, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-9078395, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-12696508, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-70772, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-4346279, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-11583193, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-724763, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-7238268, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-2169612, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-3683883, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-12892867, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-3750464, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-3222844, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-65806, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-2961726, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-2435382, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-2501944, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-9211799, bmp.GetPixel(160, 160).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap24bitData() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(-3355456, bmp.GetPixel(163, 1).ToArgb()); + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(520, data.Stride); + Assert.Equal(183, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(217, *(scan + 0)); + Assert.Equal(192, *(scan + 1009)); + Assert.Equal(210, *(scan + 2018)); + Assert.Equal(196, *(scan + 3027)); + Assert.Equal(216, *(scan + 4036)); + Assert.Equal(215, *(scan + 5045)); + Assert.Equal(218, *(scan + 6054)); + Assert.Equal(218, *(scan + 7063)); + Assert.Equal(95, *(scan + 8072)); + Assert.Equal(9, *(scan + 9081)); + Assert.Equal(247, *(scan + 10090)); + Assert.Equal(161, *(scan + 11099)); + Assert.Equal(130, *(scan + 12108)); + Assert.Equal(131, *(scan + 13117)); + Assert.Equal(175, *(scan + 14126)); + Assert.Equal(217, *(scan + 15135)); + Assert.Equal(201, *(scan + 16144)); + Assert.Equal(183, *(scan + 17153)); + Assert.Equal(236, *(scan + 18162)); + Assert.Equal(242, *(scan + 19171)); + Assert.Equal(125, *(scan + 20180)); + Assert.Equal(193, *(scan + 21189)); + Assert.Equal(227, *(scan + 22198)); + Assert.Equal(44, *(scan + 23207)); + Assert.Equal(230, *(scan + 24216)); + Assert.Equal(224, *(scan + 25225)); + Assert.Equal(164, *(scan + 26234)); + Assert.Equal(43, *(scan + 27243)); + Assert.Equal(200, *(scan + 28252)); + Assert.Equal(255, *(scan + 29261)); + Assert.Equal(226, *(scan + 30270)); + Assert.Equal(230, *(scan + 31279)); + Assert.Equal(178, *(scan + 32288)); + Assert.Equal(224, *(scan + 33297)); + Assert.Equal(233, *(scan + 34306)); + Assert.Equal(212, *(scan + 35315)); + Assert.Equal(153, *(scan + 36324)); + Assert.Equal(143, *(scan + 37333)); + Assert.Equal(215, *(scan + 38342)); + Assert.Equal(116, *(scan + 39351)); + Assert.Equal(26, *(scan + 40360)); + Assert.Equal(28, *(scan + 41369)); + Assert.Equal(75, *(scan + 42378)); + Assert.Equal(50, *(scan + 43387)); + Assert.Equal(244, *(scan + 44396)); + Assert.Equal(191, *(scan + 45405)); + Assert.Equal(200, *(scan + 46414)); + Assert.Equal(197, *(scan + 47423)); + Assert.Equal(232, *(scan + 48432)); + Assert.Equal(186, *(scan + 49441)); + Assert.Equal(210, *(scan + 50450)); + Assert.Equal(215, *(scan + 51459)); + Assert.Equal(155, *(scan + 52468)); + Assert.Equal(56, *(scan + 53477)); + Assert.Equal(149, *(scan + 54486)); + Assert.Equal(137, *(scan + 55495)); + Assert.Equal(141, *(scan + 56504)); + Assert.Equal(36, *(scan + 57513)); + Assert.Equal(39, *(scan + 58522)); + Assert.Equal(25, *(scan + 59531)); + Assert.Equal(44, *(scan + 60540)); + Assert.Equal(12, *(scan + 61549)); + Assert.Equal(161, *(scan + 62558)); + Assert.Equal(179, *(scan + 63567)); + Assert.Equal(181, *(scan + 64576)); + Assert.Equal(165, *(scan + 65585)); + Assert.Equal(182, *(scan + 66594)); + Assert.Equal(186, *(scan + 67603)); + Assert.Equal(201, *(scan + 68612)); + Assert.Equal(49, *(scan + 69621)); + Assert.Equal(161, *(scan + 70630)); + Assert.Equal(140, *(scan + 71639)); + Assert.Equal(2, *(scan + 72648)); + Assert.Equal(15, *(scan + 73657)); + Assert.Equal(33, *(scan + 74666)); + Assert.Equal(17, *(scan + 75675)); + Assert.Equal(0, *(scan + 76684)); + Assert.Equal(47, *(scan + 77693)); + Assert.Equal(4, *(scan + 78702)); + Assert.Equal(142, *(scan + 79711)); + Assert.Equal(151, *(scan + 80720)); + Assert.Equal(124, *(scan + 81729)); + Assert.Equal(81, *(scan + 82738)); + Assert.Equal(214, *(scan + 83747)); + Assert.Equal(217, *(scan + 84756)); + Assert.Equal(30, *(scan + 85765)); + Assert.Equal(185, *(scan + 86774)); + Assert.Equal(200, *(scan + 87783)); + Assert.Equal(37, *(scan + 88792)); + Assert.Equal(2, *(scan + 89801)); + Assert.Equal(41, *(scan + 90810)); + Assert.Equal(16, *(scan + 91819)); + Assert.Equal(0, *(scan + 92828)); + Assert.Equal(146, *(scan + 93837)); + Assert.Equal(163, *(scan + 94846)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + /* Checks bitmap features on a known 32-bits bitmap (codec)*/ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(173, bmp.Width); + Assert.Equal(183, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(173, rect.Width); + Assert.Equal(183, rect.Height); + + Assert.Equal(173, bmp.Size.Width); + Assert.Equal(183, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(PixelFormat.Format32bppRgb, bmp.PixelFormat); + // sampling values from a well known bitmap + Assert.Equal(-1579559, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-1645353, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-461332, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-330005, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-2237489, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-1251105, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-3024947, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-2699070, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-2366734, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-4538413, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-6116681, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-7369076, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-13024729, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-7174020, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-51, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16053503, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-8224431, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16579326, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-2502457, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-9078395, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-12696508, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-70772, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-4346279, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-11583193, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-724763, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-7238268, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-2169612, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-3683883, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-12892867, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-3750464, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-3222844, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-65806, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-2961726, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-2435382, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-2501944, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-9211799, bmp.GetPixel(160, 160).ToArgb()); + } + } + + private void Save(PixelFormat original, PixelFormat expected, bool colorCheck) + { + string sOutFile = $"linerect-{expected}.bmp"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.BlueViolet, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + bmp.Save(sOutFile, ImageFormat.Bmp); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(expected, bmpLoad.PixelFormat); + if (colorCheck) + { + Color color = bmpLoad.GetPixel(10, 10); + Assert.Equal(Color.FromArgb(255, 138, 43, 226), color); + } + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format32bppRgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format32bppRgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format32bppRgb, true); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void NonInvertedBitmap() + { + // regression check against http://bugzilla.ximian.com/show_bug.cgi?id=80751 + string sInFile = Helpers.GetTestBitmapPath("non-inverted.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(90, bmp.Width); + Assert.Equal(60, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(90, rect.Width); + Assert.Equal(60, rect.Height); + + Assert.Equal(90, bmp.Size.Width); + Assert.Equal(60, bmp.Size.Height); + + // sampling values from a well known bitmap + Assert.Equal(-16777216, bmp.GetPixel(12, 21).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(21, 37).ToArgb()); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/GifCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/GifCodecTests.cs new file mode 100644 index 00000000000..6813a4ded3f --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/GifCodecTests.cs @@ -0,0 +1,267 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// GIF Codec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// Copyright (C) 2006, 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + public class GifCodecTest + { + /* Checks bitmap features on a known 1bbp bitmap */ + private void Bitmap8bitsFeatures(string filename) + { + using (Bitmap bmp = new Bitmap(filename)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format8bppIndexed, bmp.PixelFormat); + Assert.Equal(110, bmp.Width); + Assert.Equal(100, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(110, rect.Width); + Assert.Equal(100, rect.Height); + + Assert.Equal(110, bmp.Size.Width); + Assert.Equal(100, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitsFeatures_Gif89() + { + Bitmap8bitsFeatures(Helpers.GetTestBitmapPath("nature24bits.gif")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitsFeatures_Gif87() + { + Bitmap8bitsFeatures(Helpers.GetTestBitmapPath("nature24bits87.gif")); + } + + private void Bitmap8bitsPixels(string filename) + { + using (Bitmap bmp = new Bitmap(filename)) + { + // sampling values from a well known bitmap + Assert.Equal(-10644802, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-12630705, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-14537409, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-14672099, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-526863, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-10263970, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-10461317, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-9722415, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-131076, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-2702435, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-6325922, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-12411924, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-131076, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-7766649, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-11512986, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-12616230, bmp.GetPixel(96, 96).ToArgb()); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitsPixels_Gif89() + { + Bitmap8bitsPixels(Helpers.GetTestBitmapPath("nature24bits.gif")); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap8bitsPixels_Gif87() + { + Bitmap8bitsPixels(Helpers.GetTestBitmapPath("nature24bits87.gif")); + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap8bitsData() + { + string sInFile = Helpers.GetTestBitmapPath("nature24bits.gif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(332, data.Stride); + Assert.Equal(100, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(190, *(scan + 0)); + Assert.Equal(217, *(scan + 1009)); + Assert.Equal(120, *(scan + 2018)); + Assert.Equal(253, *(scan + 3027)); + Assert.Equal(233, *(scan + 4036)); + Assert.Equal(176, *(scan + 5045)); + Assert.Equal(151, *(scan + 6054)); + Assert.Equal(220, *(scan + 7063)); + Assert.Equal(139, *(scan + 8072)); + Assert.Equal(121, *(scan + 9081)); + Assert.Equal(160, *(scan + 10090)); + Assert.Equal(92, *(scan + 11099)); + Assert.Equal(96, *(scan + 12108)); + Assert.Equal(64, *(scan + 13117)); + Assert.Equal(156, *(scan + 14126)); + Assert.Equal(68, *(scan + 15135)); + Assert.Equal(156, *(scan + 16144)); + Assert.Equal(84, *(scan + 17153)); + Assert.Equal(55, *(scan + 18162)); + Assert.Equal(68, *(scan + 19171)); + Assert.Equal(116, *(scan + 20180)); + Assert.Equal(61, *(scan + 21189)); + Assert.Equal(69, *(scan + 22198)); + Assert.Equal(75, *(scan + 23207)); + Assert.Equal(61, *(scan + 24216)); + Assert.Equal(66, *(scan + 25225)); + Assert.Equal(40, *(scan + 26234)); + Assert.Equal(55, *(scan + 27243)); + Assert.Equal(53, *(scan + 28252)); + Assert.Equal(215, *(scan + 29261)); + Assert.Equal(99, *(scan + 30270)); + Assert.Equal(67, *(scan + 31279)); + Assert.Equal(142, *(scan + 32288)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Interlaced() + { + string sInFile = Helpers.GetTestBitmapPath("81773-interlaced.gif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + for (int i = 0; i < 255; i++) + { + Color c = bmp.GetPixel(0, i); + Assert.Equal(255, c.A); + Assert.Equal(i, c.R); + Assert.Equal(i, c.G); + Assert.Equal(i, c.B); + } + } + } + + private void Save(PixelFormat original, PixelFormat expected, bool exactColorCheck) + { + string sOutFile = $"linerect-{expected}.gif"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.Red, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + bmp.Save(sOutFile, ImageFormat.Gif); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(expected, bmpLoad.PixelFormat); + Color color = bmpLoad.GetPixel(10, 10); + if (exactColorCheck) + { + Assert.Equal(Color.FromArgb(255, 255, 0, 0), color); + } + else + { + // FIXME: we don't save a pure red (F8 instead of FF) into the file so the color-check assert will fail + // this is due to libgif's QuantizeBuffer. An alternative would be to make our own that checks if less than 256 colors + // are used in the bitmap (or else use QuantizeBuffer). + Assert.Equal(255, color.A); + Assert.True(color.R >= 248); + Assert.Equal(0, color.G); + Assert.Equal(0, color.B); + } + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format8bppIndexed, false); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format8bppIndexed, false); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format8bppIndexed, false); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format8bppIndexed, false); + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/IconCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/IconCodecTests.cs new file mode 100644 index 00000000000..6e46b380d0f --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/IconCodecTests.cs @@ -0,0 +1,1985 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// ICO Codec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// Copyright (C) 2006-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + public class IconCodecTest + { + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Image16() + { + string sInFile = Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"); + using (Image image = Image.FromFile(sInFile)) + { + Assert.True(image.RawFormat.Equals(ImageFormat.Icon)); + // note that image is "promoted" to 32bits + Assert.Equal(PixelFormat.Format32bppArgb, image.PixelFormat); + Assert.Equal(73746, image.Flags); + + using (Bitmap bmp = new Bitmap(image)) + { + Assert.True(bmp.RawFormat.Equals(ImageFormat.MemoryBmp)); + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(2, bmp.Flags); + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Image16_PaletteEntries() + { + string sInFile = Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"); + using (Image image = Image.FromFile(sInFile)) + { + Assert.Equal(0, image.Palette.Entries.Length); + } + } + + // simley.ico has 48x48, 32x32 and 16x16 images (in that order) + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap16Features() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + // note that image is "promoted" to 32bits + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(16, bmp.Width); + Assert.Equal(16, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(16, rect.Width); + Assert.Equal(16, rect.Height); + + Assert.Equal(16, bmp.Size.Width); + Assert.Equal(16, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Flaky - ArgumentException")] + public void Bitmap16Features_Palette_Entries() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap16Pixels() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(0, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 0).ToArgb()); + Assert.Equal(-256, bmp.GetPixel(4, 4).ToArgb()); + Assert.Equal(-256, bmp.GetPixel(4, 8).ToArgb()); + Assert.Equal(-8355840, bmp.GetPixel(4, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 0).ToArgb()); + Assert.Equal(-256, bmp.GetPixel(8, 4).ToArgb()); + Assert.Equal(-256, bmp.GetPixel(8, 8).ToArgb()); + Assert.Equal(-256, bmp.GetPixel(8, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 4).ToArgb()); + Assert.Equal(-8355840, bmp.GetPixel(12, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 12).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap16Data() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_multiple_entries_4bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(16, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(0, *(scan + 0)); + Assert.Equal(0, *(scan + 13)); + Assert.Equal(0, *(scan + 26)); + Assert.Equal(0, *(scan + 39)); + Assert.Equal(0, *(scan + 52)); + Assert.Equal(0, *(scan + 65)); + Assert.Equal(0, *(scan + 78)); + Assert.Equal(0, *(scan + 91)); + Assert.Equal(0, *(scan + 104)); + Assert.Equal(0, *(scan + 117)); + Assert.Equal(0, *(scan + 130)); + Assert.Equal(0, *(scan + 143)); + Assert.Equal(0, *(scan + 156)); + Assert.Equal(255, *(scan + 169)); + Assert.Equal(0, *(scan + 182)); + Assert.Equal(0, *(scan + 195)); + Assert.Equal(255, *(scan + 208)); + Assert.Equal(255, *(scan + 221)); + Assert.Equal(0, *(scan + 234)); + Assert.Equal(128, *(scan + 247)); + Assert.Equal(0, *(scan + 260)); + Assert.Equal(0, *(scan + 273)); + Assert.Equal(0, *(scan + 286)); + Assert.Equal(255, *(scan + 299)); + Assert.Equal(0, *(scan + 312)); + Assert.Equal(128, *(scan + 325)); + Assert.Equal(0, *(scan + 338)); + Assert.Equal(0, *(scan + 351)); + Assert.Equal(255, *(scan + 364)); + Assert.Equal(0, *(scan + 377)); + Assert.Equal(0, *(scan + 390)); + Assert.Equal(255, *(scan + 403)); + Assert.Equal(255, *(scan + 416)); + Assert.Equal(0, *(scan + 429)); + Assert.Equal(255, *(scan + 442)); + Assert.Equal(0, *(scan + 455)); + Assert.Equal(0, *(scan + 468)); + Assert.Equal(0, *(scan + 481)); + Assert.Equal(255, *(scan + 494)); + Assert.Equal(0, *(scan + 507)); + Assert.Equal(0, *(scan + 520)); + Assert.Equal(0, *(scan + 533)); + Assert.Equal(0, *(scan + 546)); + Assert.Equal(255, *(scan + 559)); + Assert.Equal(0, *(scan + 572)); + Assert.Equal(0, *(scan + 585)); + Assert.Equal(255, *(scan + 598)); + Assert.Equal(0, *(scan + 611)); + Assert.Equal(0, *(scan + 624)); + Assert.Equal(0, *(scan + 637)); + Assert.Equal(128, *(scan + 650)); + Assert.Equal(0, *(scan + 663)); + Assert.Equal(0, *(scan + 676)); + Assert.Equal(0, *(scan + 689)); + Assert.Equal(0, *(scan + 702)); + Assert.Equal(0, *(scan + 715)); + Assert.Equal(0, *(scan + 728)); + Assert.Equal(0, *(scan + 741)); + Assert.Equal(0, *(scan + 754)); + Assert.Equal(0, *(scan + 767)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + // VisualPng.ico only has a 32x32 size available + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap32Features() + { + string sInFile = Helpers.GetTestBitmapPath("VisualPng.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(32, bmp.Width); + Assert.Equal(32, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(32, rect.Width); + Assert.Equal(32, rect.Height); + + Assert.Equal(32, bmp.Size.Width); + Assert.Equal(32, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32Features_PaletteEntries() + { + string sInFile = Helpers.GetTestBitmapPath("VisualPng.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32Pixels() + { + string sInFile = Helpers.GetTestBitmapPath("VisualPng.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(0, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-8388608, bmp.GetPixel(0, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 16).ToArgb()); + Assert.Equal(-65536, bmp.GetPixel(8, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 4).ToArgb()); + Assert.Equal(-8388608, bmp.GetPixel(12, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 16).ToArgb()); + Assert.Equal(-65536, bmp.GetPixel(12, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 20).ToArgb()); + Assert.Equal(-65536, bmp.GetPixel(16, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 4).ToArgb()); + Assert.Equal(-8388608, bmp.GetPixel(20, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(24, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 20).ToArgb()); + Assert.Equal(-8388608, bmp.GetPixel(28, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 28).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap32Data() + { + string sInFile = Helpers.GetTestBitmapPath("VisualPng.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(32, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(0, *(scan + 0)); + Assert.Equal(0, *(scan + 13)); + Assert.Equal(0, *(scan + 26)); + Assert.Equal(0, *(scan + 39)); + Assert.Equal(0, *(scan + 52)); + Assert.Equal(0, *(scan + 65)); + Assert.Equal(0, *(scan + 78)); + Assert.Equal(0, *(scan + 91)); + Assert.Equal(0, *(scan + 104)); + Assert.Equal(0, *(scan + 117)); + Assert.Equal(0, *(scan + 130)); + Assert.Equal(0, *(scan + 143)); + Assert.Equal(0, *(scan + 156)); + Assert.Equal(0, *(scan + 169)); + Assert.Equal(0, *(scan + 182)); + Assert.Equal(0, *(scan + 195)); + Assert.Equal(0, *(scan + 208)); + Assert.Equal(0, *(scan + 221)); + Assert.Equal(0, *(scan + 234)); + Assert.Equal(0, *(scan + 247)); + Assert.Equal(0, *(scan + 260)); + Assert.Equal(0, *(scan + 273)); + Assert.Equal(0, *(scan + 286)); + Assert.Equal(0, *(scan + 299)); + Assert.Equal(0, *(scan + 312)); + Assert.Equal(0, *(scan + 325)); + Assert.Equal(0, *(scan + 338)); + Assert.Equal(0, *(scan + 351)); + Assert.Equal(0, *(scan + 364)); + Assert.Equal(0, *(scan + 377)); + Assert.Equal(0, *(scan + 390)); + Assert.Equal(0, *(scan + 403)); + Assert.Equal(0, *(scan + 416)); + Assert.Equal(0, *(scan + 429)); + Assert.Equal(0, *(scan + 442)); + Assert.Equal(0, *(scan + 455)); + Assert.Equal(0, *(scan + 468)); + Assert.Equal(0, *(scan + 481)); + Assert.Equal(128, *(scan + 494)); + Assert.Equal(0, *(scan + 507)); + Assert.Equal(0, *(scan + 520)); + Assert.Equal(0, *(scan + 533)); + Assert.Equal(0, *(scan + 546)); + Assert.Equal(0, *(scan + 559)); + Assert.Equal(128, *(scan + 572)); + Assert.Equal(0, *(scan + 585)); + Assert.Equal(0, *(scan + 598)); + Assert.Equal(0, *(scan + 611)); + Assert.Equal(0, *(scan + 624)); + Assert.Equal(0, *(scan + 637)); + Assert.Equal(128, *(scan + 650)); + Assert.Equal(0, *(scan + 663)); + Assert.Equal(0, *(scan + 676)); + Assert.Equal(0, *(scan + 689)); + Assert.Equal(0, *(scan + 702)); + Assert.Equal(0, *(scan + 715)); + Assert.Equal(0, *(scan + 728)); + Assert.Equal(0, *(scan + 741)); + Assert.Equal(0, *(scan + 754)); + Assert.Equal(0, *(scan + 767)); + Assert.Equal(0, *(scan + 780)); + Assert.Equal(0, *(scan + 793)); + Assert.Equal(128, *(scan + 806)); + Assert.Equal(0, *(scan + 819)); + Assert.Equal(0, *(scan + 832)); + Assert.Equal(128, *(scan + 845)); + Assert.Equal(0, *(scan + 858)); + Assert.Equal(0, *(scan + 871)); + Assert.Equal(0, *(scan + 884)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + // 48x48_one_entry_1bit.ico only has a 48x48 size available + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap48Features() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(48, bmp.Width); + Assert.Equal(48, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(48, rect.Width); + Assert.Equal(48, rect.Height); + + Assert.Equal(48, bmp.Size.Width); + Assert.Equal(48, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap48Features_Palette_Entries() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap48Pixels() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-16777216, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 8).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 12).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 16).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 20).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 24).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 28).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 36).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 40).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(4, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 40).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(4, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 8).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 12).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 16).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 20).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 24).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 28).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 36).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(8, 40).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(8, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(12, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(12, 8).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 12).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 16).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 20).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 24).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 28).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 36).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(12, 40).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(12, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(16, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(16, 8).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(16, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 28).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(16, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 36).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(16, 40).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(16, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(20, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(20, 8).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(20, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 16).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(20, 20).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(20, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 28).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(20, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 36).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(20, 40).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(20, 44).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(24, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 4).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(24, 8).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(24, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 16).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap48Data() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_one_entry_1bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(48, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(0, *(scan + 0)); + Assert.Equal(0, *(scan + 13)); + Assert.Equal(0, *(scan + 26)); + Assert.Equal(0, *(scan + 39)); + Assert.Equal(0, *(scan + 52)); + Assert.Equal(0, *(scan + 65)); + Assert.Equal(0, *(scan + 78)); + Assert.Equal(0, *(scan + 91)); + Assert.Equal(0, *(scan + 104)); + Assert.Equal(0, *(scan + 117)); + Assert.Equal(0, *(scan + 130)); + Assert.Equal(0, *(scan + 143)); + Assert.Equal(0, *(scan + 156)); + Assert.Equal(0, *(scan + 169)); + Assert.Equal(0, *(scan + 182)); + Assert.Equal(0, *(scan + 195)); + Assert.Equal(0, *(scan + 208)); + Assert.Equal(0, *(scan + 221)); + Assert.Equal(0, *(scan + 234)); + Assert.Equal(0, *(scan + 247)); + Assert.Equal(0, *(scan + 260)); + Assert.Equal(0, *(scan + 273)); + Assert.Equal(0, *(scan + 286)); + Assert.Equal(255, *(scan + 299)); + Assert.Equal(255, *(scan + 312)); + Assert.Equal(255, *(scan + 325)); + Assert.Equal(255, *(scan + 338)); + Assert.Equal(255, *(scan + 351)); + Assert.Equal(255, *(scan + 364)); + Assert.Equal(255, *(scan + 377)); + Assert.Equal(255, *(scan + 390)); + Assert.Equal(255, *(scan + 403)); + Assert.Equal(255, *(scan + 416)); + Assert.Equal(0, *(scan + 429)); + Assert.Equal(255, *(scan + 442)); + Assert.Equal(255, *(scan + 455)); + Assert.Equal(255, *(scan + 468)); + Assert.Equal(255, *(scan + 481)); + Assert.Equal(255, *(scan + 494)); + Assert.Equal(255, *(scan + 507)); + Assert.Equal(255, *(scan + 520)); + Assert.Equal(255, *(scan + 533)); + Assert.Equal(255, *(scan + 546)); + Assert.Equal(255, *(scan + 559)); + Assert.Equal(0, *(scan + 572)); + Assert.Equal(255, *(scan + 585)); + Assert.Equal(0, *(scan + 598)); + Assert.Equal(0, *(scan + 611)); + Assert.Equal(0, *(scan + 624)); + Assert.Equal(0, *(scan + 637)); + Assert.Equal(0, *(scan + 650)); + Assert.Equal(0, *(scan + 663)); + Assert.Equal(0, *(scan + 676)); + Assert.Equal(0, *(scan + 689)); + Assert.Equal(0, *(scan + 702)); + Assert.Equal(0, *(scan + 715)); + Assert.Equal(255, *(scan + 728)); + Assert.Equal(0, *(scan + 741)); + Assert.Equal(0, *(scan + 754)); + Assert.Equal(0, *(scan + 767)); + Assert.Equal(0, *(scan + 780)); + Assert.Equal(0, *(scan + 793)); + Assert.Equal(0, *(scan + 806)); + Assert.Equal(0, *(scan + 819)); + Assert.Equal(0, *(scan + 832)); + Assert.Equal(0, *(scan + 845)); + Assert.Equal(0, *(scan + 858)); + Assert.Equal(255, *(scan + 871)); + Assert.Equal(0, *(scan + 884)); + Assert.Equal(0, *(scan + 897)); + Assert.Equal(0, *(scan + 910)); + Assert.Equal(0, *(scan + 923)); + Assert.Equal(0, *(scan + 936)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + // 64x64x256 only has a 64x64 size available + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap64Features() + { + string sInFile = Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(64, bmp.Width); + Assert.Equal(64, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(64, rect.Width); + Assert.Equal(64, rect.Height); + + Assert.Equal(64, bmp.Size.Width); + Assert.Equal(64, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap64Features_Palette_Entries() + { + string sInFile = Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap64Pixels() + { + string sInFile = Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-65383, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 4).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 8).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 12).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 16).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 20).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 24).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 28).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 36).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 40).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 44).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 48).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 52).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 56).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(0, 60).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(4, 0).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 4).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 8).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 12).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 16).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 20).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 24).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 28).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 32).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 36).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 40).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 44).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 48).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 52).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(4, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 60).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(8, 0).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(8, 4).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 8).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 12).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 16).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 20).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 24).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 28).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 32).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 36).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 40).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 44).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 48).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(8, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 60).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(12, 0).ToArgb()); + Assert.Equal(-10079335, bmp.GetPixel(12, 4).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(12, 8).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 12).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 16).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 20).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 24).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 28).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 32).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 36).ToArgb()); + Assert.Equal(-33664, bmp.GetPixel(12, 40).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap64Data() + { + string sInFile = Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(64, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(153, *(scan + 0)); + Assert.Equal(0, *(scan + 97)); + Assert.Equal(255, *(scan + 194)); + Assert.Equal(0, *(scan + 291)); + Assert.Equal(0, *(scan + 388)); + Assert.Equal(204, *(scan + 485)); + Assert.Equal(204, *(scan + 582)); + Assert.Equal(0, *(scan + 679)); + Assert.Equal(204, *(scan + 776)); + Assert.Equal(153, *(scan + 873)); + Assert.Equal(0, *(scan + 970)); + Assert.Equal(0, *(scan + 1067)); + Assert.Equal(153, *(scan + 1164)); + Assert.Equal(153, *(scan + 1261)); + Assert.Equal(102, *(scan + 1358)); + Assert.Equal(0, *(scan + 1455)); + Assert.Equal(0, *(scan + 1552)); + Assert.Equal(204, *(scan + 1649)); + Assert.Equal(153, *(scan + 1746)); + Assert.Equal(0, *(scan + 1843)); + Assert.Equal(0, *(scan + 1940)); + Assert.Equal(51, *(scan + 2037)); + Assert.Equal(0, *(scan + 2134)); + Assert.Equal(0, *(scan + 2231)); + Assert.Equal(102, *(scan + 2328)); + Assert.Equal(124, *(scan + 2425)); + Assert.Equal(204, *(scan + 2522)); + Assert.Equal(0, *(scan + 2619)); + Assert.Equal(0, *(scan + 2716)); + Assert.Equal(204, *(scan + 2813)); + Assert.Equal(51, *(scan + 2910)); + Assert.Equal(0, *(scan + 3007)); + Assert.Equal(255, *(scan + 3104)); + Assert.Equal(0, *(scan + 3201)); + Assert.Equal(0, *(scan + 3298)); + Assert.Equal(0, *(scan + 3395)); + Assert.Equal(128, *(scan + 3492)); + Assert.Equal(0, *(scan + 3589)); + Assert.Equal(255, *(scan + 3686)); + Assert.Equal(128, *(scan + 3783)); + Assert.Equal(0, *(scan + 3880)); + Assert.Equal(128, *(scan + 3977)); + Assert.Equal(0, *(scan + 4074)); + Assert.Equal(0, *(scan + 4171)); + Assert.Equal(204, *(scan + 4268)); + Assert.Equal(0, *(scan + 4365)); + Assert.Equal(0, *(scan + 4462)); + Assert.Equal(102, *(scan + 4559)); + Assert.Equal(0, *(scan + 4656)); + Assert.Equal(0, *(scan + 4753)); + Assert.Equal(102, *(scan + 4850)); + Assert.Equal(0, *(scan + 4947)); + Assert.Equal(0, *(scan + 5044)); + Assert.Equal(204, *(scan + 5141)); + Assert.Equal(128, *(scan + 5238)); + Assert.Equal(0, *(scan + 5335)); + Assert.Equal(128, *(scan + 5432)); + Assert.Equal(128, *(scan + 5529)); + Assert.Equal(0, *(scan + 5626)); + Assert.Equal(255, *(scan + 5723)); + Assert.Equal(153, *(scan + 5820)); + Assert.Equal(0, *(scan + 5917)); + Assert.Equal(0, *(scan + 6014)); + Assert.Equal(51, *(scan + 6111)); + Assert.Equal(0, *(scan + 6208)); + Assert.Equal(255, *(scan + 6305)); + Assert.Equal(153, *(scan + 6402)); + Assert.Equal(0, *(scan + 6499)); + Assert.Equal(153, *(scan + 6596)); + Assert.Equal(102, *(scan + 6693)); + Assert.Equal(0, *(scan + 6790)); + Assert.Equal(204, *(scan + 6887)); + Assert.Equal(153, *(scan + 6984)); + Assert.Equal(0, *(scan + 7081)); + Assert.Equal(204, *(scan + 7178)); + Assert.Equal(153, *(scan + 7275)); + Assert.Equal(0, *(scan + 7372)); + Assert.Equal(0, *(scan + 7469)); + Assert.Equal(153, *(scan + 7566)); + Assert.Equal(0, *(scan + 7663)); + Assert.Equal(0, *(scan + 7760)); + Assert.Equal(153, *(scan + 7857)); + Assert.Equal(102, *(scan + 7954)); + Assert.Equal(102, *(scan + 8051)); + Assert.Equal(0, *(scan + 8148)); + Assert.Equal(0, *(scan + 8245)); + Assert.Equal(0, *(scan + 8342)); + Assert.Equal(204, *(scan + 8439)); + Assert.Equal(0, *(scan + 8536)); + Assert.Equal(204, *(scan + 8633)); + Assert.Equal(128, *(scan + 8730)); + Assert.Equal(0, *(scan + 8827)); + Assert.Equal(0, *(scan + 8924)); + Assert.Equal(153, *(scan + 9021)); + Assert.Equal(153, *(scan + 9118)); + Assert.Equal(255, *(scan + 9215)); + Assert.Equal(0, *(scan + 9312)); + Assert.Equal(0, *(scan + 9409)); + Assert.Equal(204, *(scan + 9506)); + Assert.Equal(0, *(scan + 9603)); + Assert.Equal(0, *(scan + 9700)); + Assert.Equal(0, *(scan + 9797)); + Assert.Equal(128, *(scan + 9894)); + Assert.Equal(0, *(scan + 9991)); + Assert.Equal(0, *(scan + 10088)); + Assert.Equal(0, *(scan + 10185)); + Assert.Equal(102, *(scan + 10282)); + Assert.Equal(0, *(scan + 10379)); + Assert.Equal(0, *(scan + 10476)); + Assert.Equal(51, *(scan + 10573)); + Assert.Equal(204, *(scan + 10670)); + Assert.Equal(0, *(scan + 10767)); + Assert.Equal(0, *(scan + 10864)); + Assert.Equal(0, *(scan + 10961)); + Assert.Equal(153, *(scan + 11058)); + Assert.Equal(0, *(scan + 11155)); + Assert.Equal(0, *(scan + 11252)); + Assert.Equal(153, *(scan + 11349)); + Assert.Equal(51, *(scan + 11446)); + Assert.Equal(0, *(scan + 11543)); + Assert.Equal(0, *(scan + 11640)); + Assert.Equal(0, *(scan + 11737)); + Assert.Equal(204, *(scan + 11834)); + Assert.Equal(0, *(scan + 11931)); + Assert.Equal(0, *(scan + 12028)); + Assert.Equal(255, *(scan + 12125)); + Assert.Equal(153, *(scan + 12222)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + // 96x96x256.ico only has a 96x96 size available + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap96Features() + { + string sInFile = Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(96, bmp.Width); + Assert.Equal(96, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(96, rect.Width); + Assert.Equal(96, rect.Height); + + Assert.Equal(96, bmp.Size.Width); + Assert.Equal(96, bmp.Size.Height); + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap96Features_Palette_Entries() + { + string sInFile = Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap96Pixels() + { + string sInFile = Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(0, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 44).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 48).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(0, 68).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 80).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 84).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(0, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(4, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(4, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(4, 16).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(4, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(4, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(4, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(4, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(4, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 60).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(4, 64).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(4, 68).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(4, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(4, 80).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(4, 84).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(4, 88).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(4, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 0).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(8, 4).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(8, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(8, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(8, 16).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(8, 20).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(8, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(8, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 60).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(8, 64).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(8, 68).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(8, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(8, 80).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(8, 84).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(8, 88).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(8, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 0).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 4).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 16).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 20).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(12, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 28).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 56).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(12, 60).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(12, 64).ToArgb()); + Assert.Equal(-3342541, bmp.GetPixel(12, 68).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(12, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(12, 80).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(12, 84).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(12, 88).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(12, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 0).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 4).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 16).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 20).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(16, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 28).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 56).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(16, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 64).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 68).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 76).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(16, 80).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 84).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(16, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(16, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 0).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(20, 4).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(20, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(20, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(20, 16).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(20, 20).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(20, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 28).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 56).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(20, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 64).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(20, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(20, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(20, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(20, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(20, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(20, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(24, 8).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(24, 12).ToArgb()); + Assert.Equal(-3407872, bmp.GetPixel(24, 16).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(24, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 28).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 56).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(24, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(24, 64).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(24, 88).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(24, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(28, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 16).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(28, 20).ToArgb()); + Assert.Equal(-16777012, bmp.GetPixel(28, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 28).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 56).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(28, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(28, 64).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 88).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(28, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(32, 4).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(32, 8).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(32, 12).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(32, 16).ToArgb()); + Assert.Equal(-16777012, bmp.GetPixel(32, 20).ToArgb()); + Assert.Equal(-16777012, bmp.GetPixel(32, 24).ToArgb()); + Assert.Equal(-16777012, bmp.GetPixel(32, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 52).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(32, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(32, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 88).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(32, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 0).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(36, 4).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(36, 8).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(36, 12).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(36, 16).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(36, 20).ToArgb()); + Assert.Equal(-16777012, bmp.GetPixel(36, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 28).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 36).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(36, 40).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(36, 44).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(36, 48).ToArgb()); + Assert.Equal(-3368602, bmp.GetPixel(36, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(36, 64).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 88).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(36, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 0).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(40, 4).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(40, 8).ToArgb()); + Assert.Equal(-10027264, bmp.GetPixel(40, 12).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(40, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 28).ToArgb()); + Assert.Equal(-13408717, bmp.GetPixel(40, 32).ToArgb()); + Assert.Equal(-13408717, bmp.GetPixel(40, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 44).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(40, 48).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 56).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(40, 60).ToArgb()); + Assert.Equal(-26317, bmp.GetPixel(40, 64).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(40, 68).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(40, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(40, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(40, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(40, 84).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(40, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(40, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(44, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 12).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 16).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 20).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 24).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 28).ToArgb()); + Assert.Equal(-13408717, bmp.GetPixel(44, 32).ToArgb()); + Assert.Equal(-13408717, bmp.GetPixel(44, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 40).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(44, 44).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(44, 48).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(44, 52).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(44, 56).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(44, 60).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(44, 64).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 68).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(44, 72).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(44, 76).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(44, 80).ToArgb()); + Assert.Equal(-13434829, bmp.GetPixel(44, 84).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(44, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(44, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 4).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(48, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(48, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(48, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(48, 28).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(48, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 36).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(48, 40).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(48, 44).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(48, 48).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(48, 52).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(48, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 60).ToArgb()); + // Assert.Equal(1842204, bmp.GetPixel(48, 64).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(48, 68).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(48, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 80).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 84).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 88).ToArgb()); + Assert.Equal(0, bmp.GetPixel(48, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(52, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(52, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(52, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(52, 36).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(52, 40).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(52, 44).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(52, 48).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(52, 52).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(52, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(52, 60).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(52, 64).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(52, 68).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(52, 72).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(52, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(52, 80).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(52, 84).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(52, 88).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(52, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(56, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(56, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(56, 36).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(56, 40).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(56, 44).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(56, 48).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(56, 52).ToArgb()); + Assert.Equal(-13312, bmp.GetPixel(56, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(56, 60).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(56, 64).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(56, 68).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(56, 72).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(56, 76).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(56, 80).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(56, 84).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(56, 88).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(56, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(60, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 44).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(60, 48).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 52).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 56).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 60).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 64).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(60, 68).ToArgb()); + Assert.Equal(-3355546, bmp.GetPixel(60, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(60, 76).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(60, 80).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(60, 84).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(60, 88).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(60, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(64, 40).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(64, 44).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 48).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(64, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(64, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(64, 68).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(64, 80).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(64, 84).ToArgb()); + Assert.Equal(-6737101, bmp.GetPixel(64, 88).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(64, 92).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(68, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 40).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(68, 44).ToArgb()); + Assert.Equal(0, bmp.GetPixel(68, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(68, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(68, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(68, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(68, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(68, 68).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(68, 72).ToArgb()); + Assert.Equal(-16751002, bmp.GetPixel(68, 76).ToArgb()); + Assert.Equal(-16751002, bmp.GetPixel(68, 80).ToArgb()); + Assert.Equal(0, bmp.GetPixel(68, 84).ToArgb()); + Assert.Equal(0, bmp.GetPixel(68, 88).ToArgb()); + Assert.Equal(-39373, bmp.GetPixel(68, 92).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(72, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 40).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(72, 44).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(72, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 68).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(72, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(72, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(72, 80).ToArgb()); + Assert.Equal(0, bmp.GetPixel(72, 84).ToArgb()); + Assert.Equal(0, bmp.GetPixel(72, 88).ToArgb()); + Assert.Equal(-39373, bmp.GetPixel(72, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(76, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(76, 40).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(76, 44).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 68).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(76, 72).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(76, 76).ToArgb()); + Assert.Equal(0, bmp.GetPixel(76, 80).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(76, 84).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(76, 88).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(76, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(80, 0).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 36).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(80, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(80, 44).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 68).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(80, 72).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(80, 76).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(80, 80).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(80, 84).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(80, 88).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(80, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(84, 0).ToArgb()); + Assert.Equal(0, bmp.GetPixel(84, 4).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(84, 36).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(84, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(84, 44).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(84, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 68).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(84, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(84, 76).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(84, 80).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(84, 84).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(84, 88).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(84, 92).ToArgb()); + Assert.Equal(0, bmp.GetPixel(88, 0).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(88, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(88, 8).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 28).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 32).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(88, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(88, 40).ToArgb()); + Assert.Equal(-16777063, bmp.GetPixel(88, 44).ToArgb()); + Assert.Equal(0, bmp.GetPixel(88, 48).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 68).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(88, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(88, 76).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(88, 80).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(88, 84).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(88, 88).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(88, 92).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(92, 0).ToArgb()); + Assert.Equal(-3342490, bmp.GetPixel(92, 4).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(92, 8).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 12).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(92, 16).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(92, 20).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(92, 24).ToArgb()); + Assert.Equal(-52429, bmp.GetPixel(92, 28).ToArgb()); + Assert.Equal(-14935012, bmp.GetPixel(92, 32).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 36).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 40).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 44).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 48).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 52).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(92, 56).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(92, 60).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(92, 64).ToArgb()); + Assert.Equal(-6750157, bmp.GetPixel(92, 68).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 72).ToArgb()); + Assert.Equal(0, bmp.GetPixel(92, 76).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(92, 80).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(92, 84).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(92, 88).ToArgb()); + Assert.Equal(-65383, bmp.GetPixel(92, 92).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap96Data() + { + string sInFile = Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(96, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(0, *(scan + 0)); + Assert.Equal(0, *(scan + 97)); + Assert.Equal(0, *(scan + 194)); + Assert.Equal(0, *(scan + 291)); + Assert.Equal(0, *(scan + 388)); + Assert.Equal(28, *(scan + 485)); + Assert.Equal(0, *(scan + 582)); + Assert.Equal(28, *(scan + 679)); + Assert.Equal(255, *(scan + 776)); + Assert.Equal(0, *(scan + 873)); + Assert.Equal(255, *(scan + 970)); + Assert.Equal(255, *(scan + 1067)); + Assert.Equal(0, *(scan + 1164)); + Assert.Equal(255, *(scan + 1261)); + Assert.Equal(255, *(scan + 1358)); + Assert.Equal(0, *(scan + 1455)); + Assert.Equal(255, *(scan + 1552)); + Assert.Equal(255, *(scan + 1649)); + Assert.Equal(0, *(scan + 1746)); + Assert.Equal(255, *(scan + 1843)); + Assert.Equal(255, *(scan + 1940)); + Assert.Equal(0, *(scan + 2037)); + Assert.Equal(255, *(scan + 2134)); + Assert.Equal(255, *(scan + 2231)); + Assert.Equal(0, *(scan + 2328)); + Assert.Equal(255, *(scan + 2425)); + Assert.Equal(255, *(scan + 2522)); + Assert.Equal(0, *(scan + 2619)); + Assert.Equal(255, *(scan + 2716)); + Assert.Equal(255, *(scan + 2813)); + Assert.Equal(0, *(scan + 2910)); + Assert.Equal(255, *(scan + 3007)); + Assert.Equal(255, *(scan + 3104)); + Assert.Equal(0, *(scan + 3201)); + Assert.Equal(255, *(scan + 3298)); + Assert.Equal(255, *(scan + 3395)); + Assert.Equal(0, *(scan + 3492)); + Assert.Equal(0, *(scan + 3589)); + Assert.Equal(255, *(scan + 3686)); + Assert.Equal(0, *(scan + 3783)); + Assert.Equal(0, *(scan + 3880)); + Assert.Equal(255, *(scan + 3977)); + Assert.Equal(0, *(scan + 4074)); + Assert.Equal(0, *(scan + 4171)); + Assert.Equal(255, *(scan + 4268)); + Assert.Equal(0, *(scan + 4365)); + Assert.Equal(28, *(scan + 4462)); + Assert.Equal(255, *(scan + 4559)); + Assert.Equal(0, *(scan + 4656)); + Assert.Equal(51, *(scan + 4753)); + Assert.Equal(255, *(scan + 4850)); + Assert.Equal(0, *(scan + 4947)); + Assert.Equal(51, *(scan + 5044)); + Assert.Equal(255, *(scan + 5141)); + Assert.Equal(0, *(scan + 5238)); + Assert.Equal(51, *(scan + 5335)); + Assert.Equal(255, *(scan + 5432)); + Assert.Equal(0, *(scan + 5529)); + Assert.Equal(51, *(scan + 5626)); + Assert.Equal(255, *(scan + 5723)); + Assert.Equal(0, *(scan + 5820)); + Assert.Equal(51, *(scan + 5917)); + Assert.Equal(255, *(scan + 6014)); + Assert.Equal(0, *(scan + 6111)); + Assert.Equal(51, *(scan + 6208)); + Assert.Equal(255, *(scan + 6305)); + Assert.Equal(0, *(scan + 6402)); + Assert.Equal(51, *(scan + 6499)); + Assert.Equal(255, *(scan + 6596)); + Assert.Equal(0, *(scan + 6693)); + Assert.Equal(51, *(scan + 6790)); + Assert.Equal(255, *(scan + 6887)); + Assert.Equal(0, *(scan + 6984)); + Assert.Equal(51, *(scan + 7081)); + Assert.Equal(255, *(scan + 7178)); + Assert.Equal(0, *(scan + 7275)); + Assert.Equal(51, *(scan + 7372)); + Assert.Equal(255, *(scan + 7469)); + Assert.Equal(0, *(scan + 7566)); + Assert.Equal(51, *(scan + 7663)); + Assert.Equal(255, *(scan + 7760)); + Assert.Equal(0, *(scan + 7857)); + Assert.Equal(51, *(scan + 7954)); + Assert.Equal(255, *(scan + 8051)); + Assert.Equal(0, *(scan + 8148)); + Assert.Equal(51, *(scan + 8245)); + Assert.Equal(255, *(scan + 8342)); + Assert.Equal(0, *(scan + 8439)); + Assert.Equal(51, *(scan + 8536)); + Assert.Equal(28, *(scan + 8633)); + Assert.Equal(0, *(scan + 8730)); + Assert.Equal(51, *(scan + 8827)); + Assert.Equal(0, *(scan + 8924)); + Assert.Equal(0, *(scan + 9021)); + Assert.Equal(51, *(scan + 9118)); + Assert.Equal(0, *(scan + 9215)); + Assert.Equal(0, *(scan + 9312)); + Assert.Equal(51, *(scan + 9409)); + Assert.Equal(0, *(scan + 9506)); + Assert.Equal(0, *(scan + 9603)); + Assert.Equal(51, *(scan + 9700)); + Assert.Equal(0, *(scan + 9797)); + Assert.Equal(28, *(scan + 9894)); + Assert.Equal(51, *(scan + 9991)); + Assert.Equal(0, *(scan + 10088)); + Assert.Equal(0, *(scan + 10185)); + Assert.Equal(51, *(scan + 10282)); + Assert.Equal(0, *(scan + 10379)); + Assert.Equal(0, *(scan + 10476)); + Assert.Equal(51, *(scan + 10573)); + Assert.Equal(0, *(scan + 10670)); + Assert.Equal(0, *(scan + 10767)); + Assert.Equal(51, *(scan + 10864)); + Assert.Equal(204, *(scan + 10961)); + Assert.Equal(0, *(scan + 11058)); + Assert.Equal(51, *(scan + 11155)); + Assert.Equal(204, *(scan + 11252)); + Assert.Equal(0, *(scan + 11349)); + Assert.Equal(51, *(scan + 11446)); + Assert.Equal(204, *(scan + 11543)); + Assert.Equal(0, *(scan + 11640)); + Assert.Equal(51, *(scan + 11737)); + Assert.Equal(204, *(scan + 11834)); + Assert.Equal(0, *(scan + 11931)); + Assert.Equal(51, *(scan + 12028)); + Assert.Equal(204, *(scan + 12125)); + Assert.Equal(0, *(scan + 12222)); + Assert.Equal(51, *(scan + 12319)); + Assert.Equal(204, *(scan + 12416)); + Assert.Equal(28, *(scan + 12513)); + Assert.Equal(51, *(scan + 12610)); + Assert.Equal(204, *(scan + 12707)); + Assert.Equal(0, *(scan + 12804)); + Assert.Equal(28, *(scan + 12901)); + Assert.Equal(204, *(scan + 12998)); + Assert.Equal(0, *(scan + 13095)); + Assert.Equal(0, *(scan + 13192)); + Assert.Equal(204, *(scan + 13289)); + Assert.Equal(0, *(scan + 13386)); + Assert.Equal(0, *(scan + 13483)); + Assert.Equal(204, *(scan + 13580)); + Assert.Equal(0, *(scan + 13677)); + Assert.Equal(28, *(scan + 13774)); + Assert.Equal(204, *(scan + 13871)); + Assert.Equal(0, *(scan + 13968)); + Assert.Equal(0, *(scan + 14065)); + Assert.Equal(204, *(scan + 14162)); + Assert.Equal(0, *(scan + 14259)); + Assert.Equal(0, *(scan + 14356)); + Assert.Equal(204, *(scan + 14453)); + Assert.Equal(0, *(scan + 14550)); + Assert.Equal(0, *(scan + 14647)); + Assert.Equal(204, *(scan + 14744)); + Assert.Equal(0, *(scan + 14841)); + Assert.Equal(0, *(scan + 14938)); + Assert.Equal(204, *(scan + 15035)); + Assert.Equal(0, *(scan + 15132)); + Assert.Equal(0, *(scan + 15229)); + Assert.Equal(204, *(scan + 15326)); + Assert.Equal(0, *(scan + 15423)); + Assert.Equal(0, *(scan + 15520)); + Assert.Equal(204, *(scan + 15617)); + Assert.Equal(0, *(scan + 15714)); + Assert.Equal(0, *(scan + 15811)); + Assert.Equal(204, *(scan + 15908)); + Assert.Equal(0, *(scan + 16005)); + Assert.Equal(0, *(scan + 16102)); + Assert.Equal(204, *(scan + 16199)); + Assert.Equal(0, *(scan + 16296)); + Assert.Equal(0, *(scan + 16393)); + Assert.Equal(204, *(scan + 16490)); + Assert.Equal(0, *(scan + 16587)); + Assert.Equal(0, *(scan + 16684)); + Assert.Equal(204, *(scan + 16781)); + Assert.Equal(0, *(scan + 16878)); + Assert.Equal(0, *(scan + 16975)); + Assert.Equal(204, *(scan + 17072)); + Assert.Equal(0, *(scan + 17169)); + Assert.Equal(0, *(scan + 17266)); + Assert.Equal(204, *(scan + 17363)); + Assert.Equal(0, *(scan + 17460)); + Assert.Equal(0, *(scan + 17557)); + Assert.Equal(28, *(scan + 17654)); + Assert.Equal(0, *(scan + 17751)); + Assert.Equal(0, *(scan + 17848)); + Assert.Equal(0, *(scan + 17945)); + Assert.Equal(28, *(scan + 18042)); + Assert.Equal(0, *(scan + 18139)); + Assert.Equal(0, *(scan + 18236)); + Assert.Equal(51, *(scan + 18333)); + Assert.Equal(28, *(scan + 18430)); + Assert.Equal(0, *(scan + 18527)); + Assert.Equal(51, *(scan + 18624)); + Assert.Equal(0, *(scan + 18721)); + Assert.Equal(28, *(scan + 18818)); + Assert.Equal(51, *(scan + 18915)); + Assert.Equal(255, *(scan + 19012)); + Assert.Equal(51, *(scan + 19109)); + Assert.Equal(51, *(scan + 19206)); + Assert.Equal(255, *(scan + 19303)); + Assert.Equal(51, *(scan + 19400)); + Assert.Equal(51, *(scan + 19497)); + Assert.Equal(255, *(scan + 19594)); + Assert.Equal(51, *(scan + 19691)); + Assert.Equal(51, *(scan + 19788)); + Assert.Equal(255, *(scan + 19885)); + Assert.Equal(51, *(scan + 19982)); + Assert.Equal(51, *(scan + 20079)); + Assert.Equal(255, *(scan + 20176)); + Assert.Equal(51, *(scan + 20273)); + Assert.Equal(51, *(scan + 20370)); + Assert.Equal(255, *(scan + 20467)); + Assert.Equal(51, *(scan + 20564)); + Assert.Equal(51, *(scan + 20661)); + Assert.Equal(255, *(scan + 20758)); + Assert.Equal(51, *(scan + 20855)); + Assert.Equal(51, *(scan + 20952)); + Assert.Equal(255, *(scan + 21049)); + Assert.Equal(51, *(scan + 21146)); + Assert.Equal(51, *(scan + 21243)); + Assert.Equal(28, *(scan + 21340)); + Assert.Equal(51, *(scan + 21437)); + Assert.Equal(51, *(scan + 21534)); + Assert.Equal(0, *(scan + 21631)); + Assert.Equal(51, *(scan + 21728)); + Assert.Equal(28, *(scan + 21825)); + Assert.Equal(0, *(scan + 21922)); + Assert.Equal(51, *(scan + 22019)); + Assert.Equal(28, *(scan + 22116)); + Assert.Equal(0, *(scan + 22213)); + Assert.Equal(51, *(scan + 22310)); + Assert.Equal(0, *(scan + 22407)); + Assert.Equal(0, *(scan + 22504)); + Assert.Equal(51, *(scan + 22601)); + Assert.Equal(0, *(scan + 22698)); + Assert.Equal(0, *(scan + 22795)); + Assert.Equal(51, *(scan + 22892)); + Assert.Equal(28, *(scan + 22989)); + Assert.Equal(0, *(scan + 23086)); + Assert.Equal(28, *(scan + 23183)); + Assert.Equal(153, *(scan + 23280)); + Assert.Equal(28, *(scan + 23377)); + Assert.Equal(0, *(scan + 23474)); + Assert.Equal(153, *(scan + 23571)); + Assert.Equal(28, *(scan + 23668)); + Assert.Equal(0, *(scan + 23765)); + Assert.Equal(153, *(scan + 23862)); + Assert.Equal(0, *(scan + 23959)); + Assert.Equal(28, *(scan + 24056)); + Assert.Equal(153, *(scan + 24153)); + Assert.Equal(0, *(scan + 24250)); + Assert.Equal(153, *(scan + 24347)); + Assert.Equal(153, *(scan + 24444)); + Assert.Equal(0, *(scan + 24541)); + Assert.Equal(153, *(scan + 24638)); + Assert.Equal(153, *(scan + 24735)); + Assert.Equal(0, *(scan + 24832)); + Assert.Equal(153, *(scan + 24929)); + Assert.Equal(153, *(scan + 25026)); + Assert.Equal(0, *(scan + 25123)); + Assert.Equal(153, *(scan + 25220)); + Assert.Equal(153, *(scan + 25317)); + Assert.Equal(0, *(scan + 25414)); + Assert.Equal(153, *(scan + 25511)); + Assert.Equal(153, *(scan + 25608)); + Assert.Equal(0, *(scan + 25705)); + Assert.Equal(153, *(scan + 25802)); + Assert.Equal(153, *(scan + 25899)); + Assert.Equal(0, *(scan + 25996)); + Assert.Equal(153, *(scan + 26093)); + Assert.Equal(153, *(scan + 26190)); + Assert.Equal(0, *(scan + 26287)); + Assert.Equal(153, *(scan + 26384)); + Assert.Equal(153, *(scan + 26481)); + Assert.Equal(0, *(scan + 26578)); + Assert.Equal(153, *(scan + 26675)); + Assert.Equal(153, *(scan + 26772)); + Assert.Equal(28, *(scan + 26869)); + Assert.Equal(153, *(scan + 26966)); + Assert.Equal(28, *(scan + 27063)); + Assert.Equal(28, *(scan + 27160)); + Assert.Equal(28, *(scan + 27257)); + Assert.Equal(0, *(scan + 27354)); + Assert.Equal(0, *(scan + 27451)); + Assert.Equal(0, *(scan + 27548)); + Assert.Equal(0, *(scan + 27645)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Xp32bppIconFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("48x48_multiple_entries_32bit.ico"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.True(bmp.RawFormat.Equals(ImageFormat.Icon)); + // note that image is "promoted" to 32bits + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + Assert.Equal(73746, bmp.Flags); + Assert.Equal(0, bmp.Palette.Entries.Length); + Assert.Equal(1, bmp.FrameDimensionsList.Length); + Assert.Equal(0, bmp.PropertyIdList.Length); + Assert.Equal(0, bmp.PropertyItems.Length); + Assert.Null(bmp.Tag); + Assert.Equal(96.0f, bmp.HorizontalResolution); + Assert.Equal(96.0f, bmp.VerticalResolution); + Assert.Equal(16, bmp.Width); + Assert.Equal(16, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(16, rect.Width); + Assert.Equal(16, rect.Height); + + Assert.Equal(16, bmp.Size.Width); + Assert.Equal(16, bmp.Size.Height); + } + } + + private void Save(PixelFormat original, PixelFormat expected, bool colorCheck) + { + string sOutFile = $"linerect-{expected}.ico"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.Red, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + // there's no encoder, so we're not saving a ICO but the alpha + // bit get sets so it's not like saving a bitmap either + bmp.Save(sOutFile, ImageFormat.Icon); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(ImageFormat.Png, bmpLoad.RawFormat); + Assert.Equal(expected, bmpLoad.PixelFormat); + if (colorCheck) + { + Color color = bmpLoad.GetPixel(10, 10); + Assert.Equal(Color.FromArgb(255, 255, 0, 0), color); + } + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format32bppArgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format32bppArgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format32bppArgb, true); + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/JpegCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/JpegCodecTests.cs new file mode 100644 index 00000000000..c2167bd7f0a --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/JpegCodecTests.cs @@ -0,0 +1,427 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// JpegCodec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// (C) 2004 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using Xunit; +using System.IO; + +namespace MonoTests.System.Drawing.Imaging +{ + public class JpegCodecTest + { + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap8bbpIndexedGreyscaleFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("nature-greyscale.jpg"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format8bppIndexed, bmp.PixelFormat); + Assert.Equal(110, bmp.Width); + Assert.Equal(100, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(110, rect.Width); + Assert.Equal(100, rect.Height); + + Assert.Equal(110, bmp.Size.Width); + Assert.Equal(100, bmp.Size.Height); + + Assert.Equal(110, bmp.PhysicalDimension.Width); + Assert.Equal(100, bmp.PhysicalDimension.Height); + + Assert.Equal(72, bmp.HorizontalResolution); + Assert.Equal(72, bmp.VerticalResolution); + + // This value is not consistent across Windows & Unix + // Assert.Equal(77896, bmp.Flags); + + ColorPalette cp = bmp.Palette; + Assert.Equal(256, cp.Entries.Length); + + // This value is not consistent across Windows & Unix + // Assert.Equal(0, cp.Flags); + for (int i = 0; i < 256; i++) + { + Color c = cp.Entries[i]; + Assert.Equal(0xFF, c.A); + Assert.Equal(i, c.R); + Assert.Equal(i, c.G); + Assert.Equal(i, c.B); + } + } + } + + [ConditionalFact(Helpers.GdiPlusIsAvailableNotWindows7)] + public void Bitmap8bbpIndexedGreyscalePixels() + { + string sInFile = Helpers.GetTestBitmapPath("nature-greyscale.jpg"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-7697782, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-12171706, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-14013910, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-15132391, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-328966, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-9934744, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-10263709, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-7368817, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-4276546, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-9079435, bmp.GetPixel(64, 64).ToArgb()); + // Assert.Equal(-7697782, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-8224126, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-11053225, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-9211021, bmp.GetPixel(96, 96).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotWindows7), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap8bbpIndexedGreyscaleData() + { + string sInFile = Helpers.GetTestBitmapPath("nature-greyscale.jpg"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(100, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(138, *(scan + 0)); + Assert.Equal(203, *(scan + 1009)); + Assert.Equal(156, *(scan + 2018)); + Assert.Equal(248, *(scan + 3027)); + Assert.Equal(221, *(scan + 4036)); + Assert.Equal(185, *(scan + 5045)); + Assert.Equal(128, *(scan + 6054)); + Assert.Equal(205, *(scan + 7063)); + Assert.Equal(153, *(scan + 8072)); + Assert.Equal(110, *(scan + 9081)); + Assert.Equal(163, *(scan + 10090)); + Assert.Equal(87, *(scan + 11099)); + Assert.Equal(90, *(scan + 12108)); + Assert.Equal(81, *(scan + 13117)); + // Assert.Equal(124, *(scan + 14126)); + Assert.Equal(99, *(scan + 15135)); + Assert.Equal(153, *(scan + 16144)); + Assert.Equal(57, *(scan + 17153)); + Assert.Equal(89, *(scan + 18162)); + Assert.Equal(71, *(scan + 19171)); + Assert.Equal(106, *(scan + 20180)); + Assert.Equal(55, *(scan + 21189)); + Assert.Equal(75, *(scan + 22198)); + Assert.Equal(77, *(scan + 23207)); + Assert.Equal(58, *(scan + 24216)); + Assert.Equal(69, *(scan + 25225)); + Assert.Equal(43, *(scan + 26234)); + Assert.Equal(55, *(scan + 27243)); + Assert.Equal(74, *(scan + 28252)); + Assert.Equal(145, *(scan + 29261)); + Assert.Equal(87, *(scan + 30270)); + Assert.Equal(85, *(scan + 31279)); + Assert.Equal(106, *(scan + 32288)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + /* Checks bitmap features on a known 24-bits bitmap */ + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Bitmap24bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("nature24bits.jpg"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format24bppRgb, bmp.PixelFormat); + Assert.Equal(110, bmp.Width); + Assert.Equal(100, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(110, rect.Width); + Assert.Equal(100, rect.Height); + + Assert.Equal(110, bmp.Size.Width); + Assert.Equal(100, bmp.Size.Height); + + Assert.Equal(110, bmp.PhysicalDimension.Width); + Assert.Equal(100, bmp.PhysicalDimension.Height); + + Assert.Equal(72, bmp.HorizontalResolution); + Assert.Equal(72, bmp.VerticalResolution); + + /* note: under MS flags aren't constant between executions in this case (no palette) */ + // Assert.Equal(77960, bmp.Flags); + + Assert.Equal(0, bmp.Palette.Entries.Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap24bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("nature24bits.jpg"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-10447423, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-12171958, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-15192259, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-15131110, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-395272, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-10131359, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-10984322, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-11034683, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-3163242, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-7311538, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-12149780, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-8224378, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-11053718, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-12944166, bmp.GetPixel(96, 96).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap24bitData() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(520, data.Stride); + Assert.Equal(183, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(217, *(scan + 0)); + Assert.Equal(192, *(scan + 1009)); + Assert.Equal(210, *(scan + 2018)); + Assert.Equal(196, *(scan + 3027)); + Assert.Equal(216, *(scan + 4036)); + Assert.Equal(215, *(scan + 5045)); + Assert.Equal(218, *(scan + 6054)); + Assert.Equal(218, *(scan + 7063)); + Assert.Equal(95, *(scan + 8072)); + Assert.Equal(9, *(scan + 9081)); + Assert.Equal(247, *(scan + 10090)); + Assert.Equal(161, *(scan + 11099)); + Assert.Equal(130, *(scan + 12108)); + Assert.Equal(131, *(scan + 13117)); + Assert.Equal(175, *(scan + 14126)); + Assert.Equal(217, *(scan + 15135)); + Assert.Equal(201, *(scan + 16144)); + Assert.Equal(183, *(scan + 17153)); + Assert.Equal(236, *(scan + 18162)); + Assert.Equal(242, *(scan + 19171)); + Assert.Equal(125, *(scan + 20180)); + Assert.Equal(193, *(scan + 21189)); + Assert.Equal(227, *(scan + 22198)); + Assert.Equal(44, *(scan + 23207)); + Assert.Equal(230, *(scan + 24216)); + Assert.Equal(224, *(scan + 25225)); + Assert.Equal(164, *(scan + 26234)); + Assert.Equal(43, *(scan + 27243)); + Assert.Equal(200, *(scan + 28252)); + Assert.Equal(255, *(scan + 29261)); + Assert.Equal(226, *(scan + 30270)); + Assert.Equal(230, *(scan + 31279)); + Assert.Equal(178, *(scan + 32288)); + Assert.Equal(224, *(scan + 33297)); + Assert.Equal(233, *(scan + 34306)); + Assert.Equal(212, *(scan + 35315)); + Assert.Equal(153, *(scan + 36324)); + Assert.Equal(143, *(scan + 37333)); + Assert.Equal(215, *(scan + 38342)); + Assert.Equal(116, *(scan + 39351)); + Assert.Equal(26, *(scan + 40360)); + Assert.Equal(28, *(scan + 41369)); + Assert.Equal(75, *(scan + 42378)); + Assert.Equal(50, *(scan + 43387)); + Assert.Equal(244, *(scan + 44396)); + Assert.Equal(191, *(scan + 45405)); + Assert.Equal(200, *(scan + 46414)); + Assert.Equal(197, *(scan + 47423)); + Assert.Equal(232, *(scan + 48432)); + Assert.Equal(186, *(scan + 49441)); + Assert.Equal(210, *(scan + 50450)); + Assert.Equal(215, *(scan + 51459)); + Assert.Equal(155, *(scan + 52468)); + Assert.Equal(56, *(scan + 53477)); + Assert.Equal(149, *(scan + 54486)); + Assert.Equal(137, *(scan + 55495)); + Assert.Equal(141, *(scan + 56504)); + Assert.Equal(36, *(scan + 57513)); + Assert.Equal(39, *(scan + 58522)); + Assert.Equal(25, *(scan + 59531)); + Assert.Equal(44, *(scan + 60540)); + Assert.Equal(12, *(scan + 61549)); + Assert.Equal(161, *(scan + 62558)); + Assert.Equal(179, *(scan + 63567)); + Assert.Equal(181, *(scan + 64576)); + Assert.Equal(165, *(scan + 65585)); + Assert.Equal(182, *(scan + 66594)); + Assert.Equal(186, *(scan + 67603)); + Assert.Equal(201, *(scan + 68612)); + Assert.Equal(49, *(scan + 69621)); + Assert.Equal(161, *(scan + 70630)); + Assert.Equal(140, *(scan + 71639)); + Assert.Equal(2, *(scan + 72648)); + Assert.Equal(15, *(scan + 73657)); + Assert.Equal(33, *(scan + 74666)); + Assert.Equal(17, *(scan + 75675)); + Assert.Equal(0, *(scan + 76684)); + Assert.Equal(47, *(scan + 77693)); + Assert.Equal(4, *(scan + 78702)); + Assert.Equal(142, *(scan + 79711)); + Assert.Equal(151, *(scan + 80720)); + Assert.Equal(124, *(scan + 81729)); + Assert.Equal(81, *(scan + 82738)); + Assert.Equal(214, *(scan + 83747)); + Assert.Equal(217, *(scan + 84756)); + Assert.Equal(30, *(scan + 85765)); + Assert.Equal(185, *(scan + 86774)); + Assert.Equal(200, *(scan + 87783)); + Assert.Equal(37, *(scan + 88792)); + Assert.Equal(2, *(scan + 89801)); + Assert.Equal(41, *(scan + 90810)); + Assert.Equal(16, *(scan + 91819)); + Assert.Equal(0, *(scan + 92828)); + Assert.Equal(146, *(scan + 93837)); + Assert.Equal(163, *(scan + 94846)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + private void Save(PixelFormat original, PixelFormat expected) + { + string sOutFile = $"linerect-{expected}.jpeg"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.Red, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + bmp.Save(sOutFile, ImageFormat.Jpeg); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(expected, bmpLoad.PixelFormat); + Color color = bmpLoad.GetPixel(10, 10); + // by default JPEG isn't lossless - so value is "near" read + Assert.True(color.R >= 195); + Assert.True(color.G < 60); + Assert.True(color.B < 60); + Assert.Equal(0xFF, color.A); + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format24bppRgb); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format24bppRgb); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format24bppRgb); + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/PngCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/PngCodecTests.cs new file mode 100644 index 00000000000..28b2eb6618b --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/PngCodecTests.cs @@ -0,0 +1,654 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// PNG Codec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// Copyright (C) 2006, 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported))] + public class PngCodecTest + { + /* Checks bitmap features on a known 1bbp bitmap */ + [Fact] + public void Bitmap1bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("1bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format1bppIndexed, bmp.PixelFormat); + + Assert.Equal(0, bmp.Palette.Flags); + Assert.Equal(2, bmp.Palette.Entries.Length); + Assert.Equal(-16777216, bmp.Palette.Entries[0].ToArgb()); + Assert.Equal(-1, bmp.Palette.Entries[1].ToArgb()); + + Assert.Equal(288, bmp.Width); + Assert.Equal(384, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(288, rect.Width); + Assert.Equal(384, rect.Height); + + Assert.Equal(288, bmp.Size.Width); + Assert.Equal(384, bmp.Size.Height); + } + } + + [Fact] + public void Bitmap1bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("1bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-1, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 192).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 224).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 256).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 288).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(0, 320).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(0, 352).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 192).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 224).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 256).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 288).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 320).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(32, 352).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 192).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 224).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 256).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 288).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 320).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 352).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 192).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 224).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 256).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(96, 288).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 320).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(96, 352).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 192).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 224).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(128, 256).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 288).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 320).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(128, 352).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(160, 160).ToArgb()); + } + } + + [Fact] + public void Bitmap1bitData() + { + string sInFile = Helpers.GetTestBitmapPath("1bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(864, data.Stride); + Assert.Equal(384, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(255, *(scan + 0)); + Assert.Equal(255, *(scan + 1009)); + Assert.Equal(255, *(scan + 2018)); + Assert.Equal(255, *(scan + 3027)); + Assert.Equal(255, *(scan + 4036)); + Assert.Equal(255, *(scan + 5045)); + Assert.Equal(255, *(scan + 6054)); + Assert.Equal(255, *(scan + 7063)); + Assert.Equal(255, *(scan + 8072)); + Assert.Equal(255, *(scan + 9081)); + Assert.Equal(255, *(scan + 10090)); + Assert.Equal(0, *(scan + 11099)); + Assert.Equal(255, *(scan + 12108)); + Assert.Equal(255, *(scan + 13117)); + Assert.Equal(0, *(scan + 14126)); + Assert.Equal(255, *(scan + 15135)); + Assert.Equal(255, *(scan + 16144)); + Assert.Equal(0, *(scan + 17153)); + Assert.Equal(0, *(scan + 18162)); + Assert.Equal(255, *(scan + 19171)); + Assert.Equal(0, *(scan + 20180)); + Assert.Equal(255, *(scan + 21189)); + Assert.Equal(255, *(scan + 22198)); + Assert.Equal(0, *(scan + 23207)); + Assert.Equal(0, *(scan + 24216)); + Assert.Equal(0, *(scan + 25225)); + Assert.Equal(0, *(scan + 26234)); + Assert.Equal(255, *(scan + 27243)); + Assert.Equal(255, *(scan + 28252)); + Assert.Equal(0, *(scan + 29261)); + Assert.Equal(255, *(scan + 30270)); + Assert.Equal(0, *(scan + 31279)); + Assert.Equal(0, *(scan + 32288)); + Assert.Equal(255, *(scan + 33297)); + Assert.Equal(255, *(scan + 34306)); + Assert.Equal(255, *(scan + 35315)); + Assert.Equal(255, *(scan + 36324)); + Assert.Equal(0, *(scan + 37333)); + Assert.Equal(255, *(scan + 38342)); + Assert.Equal(255, *(scan + 39351)); + Assert.Equal(255, *(scan + 40360)); + Assert.Equal(255, *(scan + 41369)); + Assert.Equal(255, *(scan + 42378)); + Assert.Equal(0, *(scan + 43387)); + Assert.Equal(0, *(scan + 44396)); + Assert.Equal(255, *(scan + 45405)); + Assert.Equal(255, *(scan + 46414)); + Assert.Equal(255, *(scan + 47423)); + Assert.Equal(255, *(scan + 48432)); + Assert.Equal(255, *(scan + 49441)); + Assert.Equal(0, *(scan + 50450)); + Assert.Equal(0, *(scan + 51459)); + Assert.Equal(255, *(scan + 52468)); + Assert.Equal(255, *(scan + 53477)); + Assert.Equal(255, *(scan + 54486)); + Assert.Equal(0, *(scan + 55495)); + Assert.Equal(0, *(scan + 56504)); + Assert.Equal(0, *(scan + 57513)); + Assert.Equal(255, *(scan + 58522)); + Assert.Equal(255, *(scan + 59531)); + Assert.Equal(0, *(scan + 60540)); + Assert.Equal(0, *(scan + 61549)); + Assert.Equal(0, *(scan + 62558)); + Assert.Equal(0, *(scan + 63567)); + Assert.Equal(255, *(scan + 64576)); + Assert.Equal(0, *(scan + 65585)); + Assert.Equal(255, *(scan + 66594)); + Assert.Equal(255, *(scan + 67603)); + Assert.Equal(0, *(scan + 68612)); + Assert.Equal(0, *(scan + 69621)); + Assert.Equal(0, *(scan + 70630)); + Assert.Equal(0, *(scan + 71639)); + Assert.Equal(0, *(scan + 72648)); + Assert.Equal(255, *(scan + 73657)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + /* Checks bitmap features on a known 2bbp bitmap */ + [Fact] + public void Bitmap2bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("81674-2bpp.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + // quite a promotion! (2 -> 32) + Assert.Equal(PixelFormat.Format32bppArgb, bmp.PixelFormat); + + // MS returns a random Flags value (not a good sign) + //Assert.Equal (0, bmp.Palette.Flags); + Assert.Equal(0, bmp.Palette.Entries.Length); + + Assert.Equal(100, bmp.Width); + Assert.Equal(100, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(100, rect.Width); + Assert.Equal(100, rect.Height); + + Assert.Equal(100, bmp.Size.Width); + Assert.Equal(100, bmp.Size.Height); + } + } + + [Fact] + public void Bitmap2bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("81674-2bpp.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-11249559, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-16777216, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-11249559, bmp.GetPixel(96, 96).ToArgb()); + } + } + + [Fact] + public void Bitmap2bitData() + { + string sInFile = Helpers.GetTestBitmapPath("81674-2bpp.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(300, data.Stride); + Assert.Equal(100, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(105, *(scan + 0)); + Assert.Equal(88, *(scan + 1009)); + Assert.Equal(255, *(scan + 2018)); + Assert.Equal(105, *(scan + 3027)); + Assert.Equal(88, *(scan + 4036)); + Assert.Equal(84, *(scan + 5045)); + Assert.Equal(255, *(scan + 6054)); + Assert.Equal(88, *(scan + 7063)); + Assert.Equal(84, *(scan + 8072)); + Assert.Equal(0, *(scan + 9081)); + Assert.Equal(0, *(scan + 10090)); + Assert.Equal(84, *(scan + 11099)); + Assert.Equal(0, *(scan + 12108)); + Assert.Equal(88, *(scan + 13117)); + Assert.Equal(84, *(scan + 14126)); + Assert.Equal(105, *(scan + 15135)); + Assert.Equal(88, *(scan + 16144)); + Assert.Equal(84, *(scan + 17153)); + Assert.Equal(0, *(scan + 18162)); + Assert.Equal(88, *(scan + 19171)); + Assert.Equal(84, *(scan + 20180)); + Assert.Equal(0, *(scan + 21189)); + Assert.Equal(88, *(scan + 22198)); + Assert.Equal(84, *(scan + 23207)); + Assert.Equal(105, *(scan + 24216)); + Assert.Equal(88, *(scan + 25225)); + Assert.Equal(0, *(scan + 26234)); + Assert.Equal(105, *(scan + 27243)); + Assert.Equal(88, *(scan + 28252)); + Assert.Equal(84, *(scan + 29261)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + /* Checks bitmap features on a known 4bbp bitmap */ + [Fact] + public void Bitmap4bitFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("4bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + + Assert.Equal(PixelFormat.Format4bppIndexed, bmp.PixelFormat); + + Assert.Equal(0, bmp.Palette.Flags); + Assert.Equal(16, bmp.Palette.Entries.Length); + Assert.Equal(-12106173, bmp.Palette.Entries[0].ToArgb()); + Assert.Equal(-10979957, bmp.Palette.Entries[1].ToArgb()); + Assert.Equal(-8879241, bmp.Palette.Entries[2].ToArgb()); + Assert.Equal(-10381134, bmp.Palette.Entries[3].ToArgb()); + Assert.Equal(-7441574, bmp.Palette.Entries[4].ToArgb()); + Assert.Equal(-6391673, bmp.Palette.Entries[5].ToArgb()); + Assert.Equal(-5861009, bmp.Palette.Entries[6].ToArgb()); + Assert.Equal(-3824008, bmp.Palette.Entries[7].ToArgb()); + Assert.Equal(-5790569, bmp.Palette.Entries[8].ToArgb()); + Assert.Equal(-6178617, bmp.Palette.Entries[9].ToArgb()); + Assert.Equal(-4668490, bmp.Palette.Entries[10].ToArgb()); + Assert.Equal(-5060143, bmp.Palette.Entries[11].ToArgb()); + Assert.Equal(-3492461, bmp.Palette.Entries[12].ToArgb()); + Assert.Equal(-2967099, bmp.Palette.Entries[13].ToArgb()); + Assert.Equal(-2175574, bmp.Palette.Entries[14].ToArgb()); + Assert.Equal(-1314578, bmp.Palette.Entries[15].ToArgb()); + + Assert.Equal(288, bmp.Width); + Assert.Equal(384, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(288, rect.Width); + Assert.Equal(384, rect.Height); + + Assert.Equal(288, bmp.Size.Width); + Assert.Equal(384, bmp.Size.Height); + } + } + + [Fact] + public void Bitmap4bitPixels() + { + string sInFile = Helpers.GetTestBitmapPath("4bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-10381134, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-3824008, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(0, 192).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(0, 224).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(0, 256).ToArgb()); + Assert.Equal(-7441574, bmp.GetPixel(0, 288).ToArgb()); + Assert.Equal(-3492461, bmp.GetPixel(0, 320).ToArgb()); + Assert.Equal(-5861009, bmp.GetPixel(0, 352).ToArgb()); + Assert.Equal(-10381134, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-7441574, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(32, 192).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(32, 224).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(32, 256).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(32, 288).ToArgb()); + Assert.Equal(-3492461, bmp.GetPixel(32, 320).ToArgb()); + Assert.Equal(-2175574, bmp.GetPixel(32, 352).ToArgb()); + Assert.Equal(-6178617, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 192).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 224).ToArgb()); + Assert.Equal(-5790569, bmp.GetPixel(64, 256).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 288).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(64, 320).ToArgb()); + Assert.Equal(-5790569, bmp.GetPixel(64, 352).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-10381134, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-7441574, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-5790569, bmp.GetPixel(96, 192).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(96, 224).ToArgb()); + Assert.Equal(-4668490, bmp.GetPixel(96, 256).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(96, 288).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(96, 320).ToArgb()); + Assert.Equal(-3492461, bmp.GetPixel(96, 352).ToArgb()); + Assert.Equal(-5861009, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-7441574, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-7441574, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 192).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 224).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 256).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 288).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 320).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(128, 352).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-1314578, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-5790569, bmp.GetPixel(160, 160).ToArgb()); + Assert.Equal(-12106173, bmp.GetPixel(160, 192).ToArgb()); + } + } + + [Fact] + public void Bitmap4bitData() + { + string sInFile = Helpers.GetTestBitmapPath("4bit.png"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(864, data.Stride); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(178, *(scan + 0)); + Assert.Equal(184, *(scan + 1009)); + Assert.Equal(235, *(scan + 2018)); + Assert.Equal(209, *(scan + 3027)); + Assert.Equal(240, *(scan + 4036)); + Assert.Equal(142, *(scan + 5045)); + Assert.Equal(139, *(scan + 6054)); + Assert.Equal(152, *(scan + 7063)); + Assert.Equal(235, *(scan + 8072)); + Assert.Equal(209, *(scan + 9081)); + Assert.Equal(240, *(scan + 10090)); + Assert.Equal(142, *(scan + 11099)); + Assert.Equal(199, *(scan + 12108)); + Assert.Equal(201, *(scan + 13117)); + Assert.Equal(97, *(scan + 14126)); + Assert.Equal(238, *(scan + 15135)); + Assert.Equal(240, *(scan + 16144)); + Assert.Equal(158, *(scan + 17153)); + Assert.Equal(119, *(scan + 18162)); + Assert.Equal(201, *(scan + 19171)); + Assert.Equal(88, *(scan + 20180)); + Assert.Equal(238, *(scan + 21189)); + Assert.Equal(240, *(scan + 22198)); + Assert.Equal(120, *(scan + 23207)); + Assert.Equal(182, *(scan + 24216)); + Assert.Equal(70, *(scan + 25225)); + Assert.Equal(71, *(scan + 26234)); + Assert.Equal(238, *(scan + 27243)); + Assert.Equal(240, *(scan + 28252)); + Assert.Equal(120, *(scan + 29261)); + Assert.Equal(238, *(scan + 30270)); + Assert.Equal(70, *(scan + 31279)); + Assert.Equal(71, *(scan + 32288)); + Assert.Equal(238, *(scan + 33297)); + Assert.Equal(240, *(scan + 34306)); + Assert.Equal(210, *(scan + 35315)); + Assert.Equal(238, *(scan + 36324)); + Assert.Equal(70, *(scan + 37333)); + Assert.Equal(97, *(scan + 38342)); + Assert.Equal(238, *(scan + 39351)); + Assert.Equal(240, *(scan + 40360)); + Assert.Equal(235, *(scan + 41369)); + Assert.Equal(238, *(scan + 42378)); + Assert.Equal(117, *(scan + 43387)); + Assert.Equal(158, *(scan + 44396)); + Assert.Equal(170, *(scan + 45405)); + Assert.Equal(240, *(scan + 46414)); + Assert.Equal(235, *(scan + 47423)); + Assert.Equal(209, *(scan + 48432)); + Assert.Equal(120, *(scan + 49441)); + Assert.Equal(71, *(scan + 50450)); + Assert.Equal(119, *(scan + 51459)); + Assert.Equal(240, *(scan + 52468)); + Assert.Equal(235, *(scan + 53477)); + Assert.Equal(209, *(scan + 54486)); + Assert.Equal(70, *(scan + 55495)); + Assert.Equal(71, *(scan + 56504)); + Assert.Equal(67, *(scan + 57513)); + Assert.Equal(240, *(scan + 58522)); + Assert.Equal(167, *(scan + 59531)); + Assert.Equal(67, *(scan + 60540)); + Assert.Equal(70, *(scan + 61549)); + Assert.Equal(71, *(scan + 62558)); + Assert.Equal(67, *(scan + 63567)); + Assert.Equal(240, *(scan + 64576)); + Assert.Equal(120, *(scan + 65585)); + Assert.Equal(182, *(scan + 66594)); + Assert.Equal(70, *(scan + 67603)); + Assert.Equal(120, *(scan + 68612)); + Assert.Equal(67, *(scan + 69621)); + Assert.Equal(70, *(scan + 70630)); + Assert.Equal(71, *(scan + 71639)); + Assert.Equal(90, *(scan + 72648)); + Assert.Equal(240, *(scan + 73657)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + private void Save(PixelFormat original, PixelFormat expected, bool colorCheck) + { + string sOutFile = $"linerect-{expected}.png"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.BlueViolet, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + bmp.Save(sOutFile, ImageFormat.Png); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(expected, bmpLoad.PixelFormat); + if (colorCheck) + { + Color color = bmpLoad.GetPixel(10, 10); + Assert.Equal(Color.FromArgb(255, 138, 43, 226), color); + } + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [Fact] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb, true); + } + + [Fact] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format32bppArgb, true); + } + + [Fact] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format32bppArgb, true); + } + + [Fact] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format32bppArgb, true); + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/TiffCodecTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/TiffCodecTests.cs new file mode 100644 index 00000000000..3ab210e55bd --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing.Imaging/TiffCodecTests.cs @@ -0,0 +1,314 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// TIFF Codec class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Sebastien Pouliot +// +// Copyright (C) 2006, 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + public class TiffCodecTest + { + /* Checks bitmap features on a known 32bbp bitmap */ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32bitsFeatures() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.tif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + GraphicsUnit unit = GraphicsUnit.World; + RectangleF rect = bmp.GetBounds(ref unit); + // MS reports 24 bpp while we report 32 bpp + // Assert.Equal (PixelFormat.Format24bppRgb, bmp.PixelFormat); + Assert.Equal(173, bmp.Width); + Assert.Equal(183, bmp.Height); + + Assert.Equal(0, rect.X); + Assert.Equal(0, rect.Y); + Assert.Equal(173, rect.Width); + Assert.Equal(183, rect.Height); + + Assert.Equal(173, bmp.Size.Width); + Assert.Equal(183, bmp.Size.Height); + } + } + + /* Checks bitmap features on a known 32bbp bitmap */ + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32bitsPixelFormat() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.tif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // GDI+ reports 24 bpp while libgdiplus reports 32 bpp + Assert.Equal (PixelFormat.Format24bppRgb, bmp.PixelFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Bitmap32bitsPixels() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.tif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + // sampling values from a well known bitmap + Assert.Equal(-1579559, bmp.GetPixel(0, 0).ToArgb()); + Assert.Equal(-1645353, bmp.GetPixel(0, 32).ToArgb()); + Assert.Equal(-461332, bmp.GetPixel(0, 64).ToArgb()); + Assert.Equal(-330005, bmp.GetPixel(0, 96).ToArgb()); + Assert.Equal(-2237489, bmp.GetPixel(0, 128).ToArgb()); + Assert.Equal(-1251105, bmp.GetPixel(0, 160).ToArgb()); + Assert.Equal(-3024947, bmp.GetPixel(32, 0).ToArgb()); + Assert.Equal(-2699070, bmp.GetPixel(32, 32).ToArgb()); + Assert.Equal(-2366734, bmp.GetPixel(32, 64).ToArgb()); + Assert.Equal(-4538413, bmp.GetPixel(32, 96).ToArgb()); + Assert.Equal(-6116681, bmp.GetPixel(32, 128).ToArgb()); + Assert.Equal(-7369076, bmp.GetPixel(32, 160).ToArgb()); + Assert.Equal(-13024729, bmp.GetPixel(64, 0).ToArgb()); + Assert.Equal(-7174020, bmp.GetPixel(64, 32).ToArgb()); + Assert.Equal(-51, bmp.GetPixel(64, 64).ToArgb()); + Assert.Equal(-16053503, bmp.GetPixel(64, 96).ToArgb()); + Assert.Equal(-8224431, bmp.GetPixel(64, 128).ToArgb()); + Assert.Equal(-16579326, bmp.GetPixel(64, 160).ToArgb()); + Assert.Equal(-2502457, bmp.GetPixel(96, 0).ToArgb()); + Assert.Equal(-9078395, bmp.GetPixel(96, 32).ToArgb()); + Assert.Equal(-12696508, bmp.GetPixel(96, 64).ToArgb()); + Assert.Equal(-70772, bmp.GetPixel(96, 96).ToArgb()); + Assert.Equal(-4346279, bmp.GetPixel(96, 128).ToArgb()); + Assert.Equal(-11583193, bmp.GetPixel(96, 160).ToArgb()); + Assert.Equal(-724763, bmp.GetPixel(128, 0).ToArgb()); + Assert.Equal(-7238268, bmp.GetPixel(128, 32).ToArgb()); + Assert.Equal(-2169612, bmp.GetPixel(128, 64).ToArgb()); + Assert.Equal(-3683883, bmp.GetPixel(128, 96).ToArgb()); + Assert.Equal(-12892867, bmp.GetPixel(128, 128).ToArgb()); + Assert.Equal(-3750464, bmp.GetPixel(128, 160).ToArgb()); + Assert.Equal(-3222844, bmp.GetPixel(160, 0).ToArgb()); + Assert.Equal(-65806, bmp.GetPixel(160, 32).ToArgb()); + Assert.Equal(-2961726, bmp.GetPixel(160, 64).ToArgb()); + Assert.Equal(-2435382, bmp.GetPixel(160, 96).ToArgb()); + Assert.Equal(-2501944, bmp.GetPixel(160, 128).ToArgb()); + Assert.Equal(-9211799, bmp.GetPixel(160, 160).ToArgb()); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void Bitmap32bitsData() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver32bits.tif"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(bmp.Height, data.Height); + Assert.Equal(bmp.Width, data.Width); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.Equal(520, data.Stride); + Assert.Equal(183, data.Height); + + unsafe + { + byte* scan = (byte*)data.Scan0; + // sampling values from a well known bitmap + Assert.Equal(217, *(scan + 0)); + Assert.Equal(192, *(scan + 1009)); + Assert.Equal(210, *(scan + 2018)); + Assert.Equal(196, *(scan + 3027)); + Assert.Equal(216, *(scan + 4036)); + Assert.Equal(215, *(scan + 5045)); + Assert.Equal(218, *(scan + 6054)); + Assert.Equal(218, *(scan + 7063)); + Assert.Equal(95, *(scan + 8072)); + Assert.Equal(9, *(scan + 9081)); + Assert.Equal(247, *(scan + 10090)); + Assert.Equal(161, *(scan + 11099)); + Assert.Equal(130, *(scan + 12108)); + Assert.Equal(131, *(scan + 13117)); + Assert.Equal(175, *(scan + 14126)); + Assert.Equal(217, *(scan + 15135)); + Assert.Equal(201, *(scan + 16144)); + Assert.Equal(183, *(scan + 17153)); + Assert.Equal(236, *(scan + 18162)); + Assert.Equal(242, *(scan + 19171)); + Assert.Equal(125, *(scan + 20180)); + Assert.Equal(193, *(scan + 21189)); + Assert.Equal(227, *(scan + 22198)); + Assert.Equal(44, *(scan + 23207)); + Assert.Equal(230, *(scan + 24216)); + Assert.Equal(224, *(scan + 25225)); + Assert.Equal(164, *(scan + 26234)); + Assert.Equal(43, *(scan + 27243)); + Assert.Equal(200, *(scan + 28252)); + Assert.Equal(255, *(scan + 29261)); + Assert.Equal(226, *(scan + 30270)); + Assert.Equal(230, *(scan + 31279)); + Assert.Equal(178, *(scan + 32288)); + Assert.Equal(224, *(scan + 33297)); + Assert.Equal(233, *(scan + 34306)); + Assert.Equal(212, *(scan + 35315)); + Assert.Equal(153, *(scan + 36324)); + Assert.Equal(143, *(scan + 37333)); + Assert.Equal(215, *(scan + 38342)); + Assert.Equal(116, *(scan + 39351)); + Assert.Equal(26, *(scan + 40360)); + Assert.Equal(28, *(scan + 41369)); + Assert.Equal(75, *(scan + 42378)); + Assert.Equal(50, *(scan + 43387)); + Assert.Equal(244, *(scan + 44396)); + Assert.Equal(191, *(scan + 45405)); + Assert.Equal(200, *(scan + 46414)); + Assert.Equal(197, *(scan + 47423)); + Assert.Equal(232, *(scan + 48432)); + Assert.Equal(186, *(scan + 49441)); + Assert.Equal(210, *(scan + 50450)); + Assert.Equal(215, *(scan + 51459)); + Assert.Equal(155, *(scan + 52468)); + Assert.Equal(56, *(scan + 53477)); + Assert.Equal(149, *(scan + 54486)); + Assert.Equal(137, *(scan + 55495)); + Assert.Equal(141, *(scan + 56504)); + Assert.Equal(36, *(scan + 57513)); + Assert.Equal(39, *(scan + 58522)); + Assert.Equal(25, *(scan + 59531)); + Assert.Equal(44, *(scan + 60540)); + Assert.Equal(12, *(scan + 61549)); + Assert.Equal(161, *(scan + 62558)); + Assert.Equal(179, *(scan + 63567)); + Assert.Equal(181, *(scan + 64576)); + Assert.Equal(165, *(scan + 65585)); + Assert.Equal(182, *(scan + 66594)); + Assert.Equal(186, *(scan + 67603)); + Assert.Equal(201, *(scan + 68612)); + Assert.Equal(49, *(scan + 69621)); + Assert.Equal(161, *(scan + 70630)); + Assert.Equal(140, *(scan + 71639)); + Assert.Equal(2, *(scan + 72648)); + Assert.Equal(15, *(scan + 73657)); + Assert.Equal(33, *(scan + 74666)); + Assert.Equal(17, *(scan + 75675)); + Assert.Equal(0, *(scan + 76684)); + Assert.Equal(47, *(scan + 77693)); + Assert.Equal(4, *(scan + 78702)); + Assert.Equal(142, *(scan + 79711)); + Assert.Equal(151, *(scan + 80720)); + Assert.Equal(124, *(scan + 81729)); + Assert.Equal(81, *(scan + 82738)); + Assert.Equal(214, *(scan + 83747)); + Assert.Equal(217, *(scan + 84756)); + Assert.Equal(30, *(scan + 85765)); + Assert.Equal(185, *(scan + 86774)); + Assert.Equal(200, *(scan + 87783)); + Assert.Equal(37, *(scan + 88792)); + Assert.Equal(2, *(scan + 89801)); + Assert.Equal(41, *(scan + 90810)); + Assert.Equal(16, *(scan + 91819)); + Assert.Equal(0, *(scan + 92828)); + Assert.Equal(146, *(scan + 93837)); + Assert.Equal(163, *(scan + 94846)); + } + } + finally + { + bmp.UnlockBits(data); + } + } + } + + private void Save(PixelFormat original, PixelFormat expected, bool colorCheck) + { + string sOutFile = $"linerect-{expected}.tif"; + + // Save + Bitmap bmp = new Bitmap(100, 100, original); + Graphics gr = Graphics.FromImage(bmp); + + using (Pen p = new Pen(Color.BlueViolet, 2)) + { + gr.DrawLine(p, 10.0F, 10.0F, 90.0F, 90.0F); + gr.DrawRectangle(p, 10.0F, 10.0F, 80.0F, 80.0F); + } + + try + { + bmp.Save(sOutFile, ImageFormat.Tiff); + + // Load + using (Bitmap bmpLoad = new Bitmap(sOutFile)) + { + Assert.Equal(expected, bmpLoad.PixelFormat); + if (colorCheck) + { + Color color = bmpLoad.GetPixel(10, 10); + Assert.Equal(Color.FromArgb(255, 138, 43, 226), color); + } + } + } + finally + { + gr.Dispose(); + bmp.Dispose(); + try + { + File.Delete(sOutFile); + } + catch + { + } + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_24bppRgb() + { + Save(PixelFormat.Format24bppRgb, PixelFormat.Format24bppRgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppRgb() + { + Save(PixelFormat.Format32bppRgb, PixelFormat.Format32bppArgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppArgb() + { + Save(PixelFormat.Format32bppArgb, PixelFormat.Format32bppArgb, true); + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void Save_32bppPArgb() + { + Save(PixelFormat.Format32bppPArgb, PixelFormat.Format32bppArgb, true); + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing/BitmapTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing/BitmapTests.cs new file mode 100644 index 00000000000..7ed2d77ee54 --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing/BitmapTests.cs @@ -0,0 +1,1411 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Bitmap class testing unit +// +// Authors: +// Jordi Mas i Hernandez (jordi@ximian.com) +// Jonathan Gilbert +// Sebastien Pouliot +// +// (C) 2004 Ximian, Inc. http://www.ximian.com +// Copyright (C) 2004,2006-2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography; +using System.Text; +using System.Xml.Serialization; +using Xunit; + +namespace MonoTests.System.Drawing +{ + public class TestBitmap + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void TestPixels() + { + // Tests GetSetPixel/SetPixel + Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppRgb); + bmp.SetPixel(0, 0, Color.FromArgb(255, 128, 128, 128)); + Color color = bmp.GetPixel(0, 0); + + Assert.Equal(Color.FromArgb(255, 128, 128, 128), color); + + bmp.SetPixel(99, 99, Color.FromArgb(255, 255, 0, 155)); + Color color2 = bmp.GetPixel(99, 99); + Assert.Equal(Color.FromArgb(255, 255, 0, 155), color2); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221")] + public void LockBits_IndexedWrite_NonIndexed() + { + using (Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format8bppIndexed)) + { + Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); + Assert.Throws(() => bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221")] + public void LockBits_NonIndexedWrite_ToIndexed() + { + using (Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppRgb)) + { + BitmapData bd = new BitmapData(); + Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); + Assert.Throws(() => bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed, bd)); + + // test to see if there's a leak or not in this case + Assert.Equal(IntPtr.Zero, bd.Scan0); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBits_ImageLockMode_Invalid() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format24bppRgb)) + { + Rectangle r = new Rectangle(4, 4, 4, 4); + BitmapData data = bmp.LockBits(r, (ImageLockMode)0, PixelFormat.Format24bppRgb); + try + { + Assert.Equal(4, data.Height); + Assert.Equal(4, data.Width); + Assert.True(data.Stride >= 12); + Assert.Equal(PixelFormat.Format24bppRgb, data.PixelFormat); + Assert.False(IntPtr.Zero.Equals(data.Scan0)); + } + finally + { + bmp.UnlockBits(data); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void LockBits_Double() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format24bppRgb)) + { + Rectangle r = new Rectangle(4, 4, 4, 4); + BitmapData data = bmp.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + Assert.Throws(() => bmp.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)); + } + finally + { + bmp.UnlockBits(data); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format1bppIndexed() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format1bppIndexed)) + { + Color c = bmp.GetPixel(0, 0); + Assert.Equal(-16777216, c.ToArgb()); + Assert.Throws(() => bmp.SetPixel(0, 0, c)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format4bppIndexed() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format4bppIndexed)) + { + Color c = bmp.GetPixel(0, 0); + Assert.Equal(-16777216, c.ToArgb()); + Assert.Throws(() => bmp.SetPixel(0, 0, c)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format8bppIndexed() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed)) + { + Color c = bmp.GetPixel(0, 0); + Assert.Equal(-16777216, c.ToArgb()); + Assert.Throws(() => bmp.SetPixel(0, 0, c)); + } + } + + private void FormatTest(PixelFormat format) + { + bool alpha = Image.IsAlphaPixelFormat(format); + int size = Image.GetPixelFormatSize(format) / 8 * 2; + using (Bitmap bmp = new Bitmap(2, 1, format)) + { + Color a = Color.FromArgb(128, 64, 32, 16); + Color b = Color.FromArgb(192, 96, 48, 24); + bmp.SetPixel(0, 0, a); + bmp.SetPixel(1, 0, b); + Color c = bmp.GetPixel(0, 0); + Color d = bmp.GetPixel(1, 0); + if (size == 4) + { + Assert.Equal(255, c.A); + Assert.Equal(66, c.R); + if (format == PixelFormat.Format16bppRgb565) + { + Assert.Equal(32, c.G); + } + else + { + Assert.Equal(33, c.G); + } + Assert.Equal(16, c.B); + + Assert.Equal(255, d.A); + Assert.Equal(99, d.R); + if (format == PixelFormat.Format16bppRgb565) + { + Assert.Equal(48, d.G); + } + else + { + Assert.Equal(49, d.G); + } + Assert.Equal(24, d.B); + } + else if (alpha) + { + if (format == PixelFormat.Format32bppPArgb) + { + Assert.Equal(a.A, c.A); + // note sure why the -1 + Assert.Equal(a.R - 1, c.R); + Assert.Equal(a.G - 1, c.G); + Assert.Equal(a.B - 1, c.B); + + Assert.Equal(b.A, d.A); + // note sure why the -1 + Assert.Equal(b.R - 1, d.R); + Assert.Equal(b.G - 1, d.G); + Assert.Equal(b.B - 1, d.B); + } + else + { + Assert.Equal(a, c); + Assert.Equal(b, d); + } + } + else + { + Assert.Equal(Color.FromArgb(255, 64, 32, 16), c); + Assert.Equal(Color.FromArgb(255, 96, 48, 24), d); + } + BitmapData bd = bmp.LockBits(new Rectangle(0, 0, 2, 1), ImageLockMode.ReadOnly, format); + try + { + byte[] data = new byte[size]; + Marshal.Copy(bd.Scan0, data, 0, size); + if (format == PixelFormat.Format32bppPArgb) + { + Assert.Equal(Math.Ceiling((float)c.B * c.A / 255), data[0]); + Assert.Equal(Math.Ceiling((float)c.G * c.A / 255), data[1]); + Assert.Equal(Math.Ceiling((float)c.R * c.A / 255), data[2]); + Assert.Equal(c.A, data[3]); + Assert.Equal(Math.Ceiling((float)d.B * d.A / 255), data[4]); + Assert.Equal(Math.Ceiling((float)d.G * d.A / 255), data[5]); + Assert.Equal(Math.Ceiling((float)d.R * d.A / 255), data[6]); + Assert.Equal(d.A, data[7]); + } + else if (size == 4) + { + int n = 0; + switch (format) + { + case PixelFormat.Format16bppRgb565: + Assert.Equal(2, data[n++]); + Assert.Equal(65, data[n++]); + Assert.Equal(131, data[n++]); + Assert.Equal(97, data[n++]); + break; + case PixelFormat.Format16bppArgb1555: + Assert.Equal(130, data[n++]); + Assert.Equal(160, data[n++]); + Assert.Equal(195, data[n++]); + Assert.Equal(176, data[n++]); + break; + case PixelFormat.Format16bppRgb555: + Assert.Equal(130, data[n++]); + Assert.Equal(32, data[n++]); + Assert.Equal(195, data[n++]); + Assert.Equal(48, data[n++]); + break; + } + } + else + { + int n = 0; + Assert.Equal(c.B, data[n++]); + Assert.Equal(c.G, data[n++]); + Assert.Equal(c.R, data[n++]); + if (size % 4 == 0) + Assert.Equal(c.A, data[n++]); + Assert.Equal(d.B, data[n++]); + Assert.Equal(d.G, data[n++]); + Assert.Equal(d.R, data[n++]); + if (size % 4 == 0) + Assert.Equal(d.A, data[n++]); + } + } + finally + { + bmp.UnlockBits(bd); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format32bppArgb() + { + FormatTest(PixelFormat.Format32bppArgb); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221")] + public void Format32bppRgb() + { + FormatTest(PixelFormat.Format32bppRgb); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format24bppRgb() + { + FormatTest(PixelFormat.Format24bppRgb); + } + + /* Get the output directory depending on the runtime and location*/ + public static string getOutSubDir() + { + string sSub, sRslt; + + if (Environment.GetEnvironmentVariable("MSNet") == null) + sSub = "mono/"; + else + sSub = "MSNet/"; + + sRslt = Path.GetFullPath(sSub); + + if (!Directory.Exists(sRslt)) + { + sRslt = "Test/System.Drawing/" + sSub; + } + + if (sRslt.Length > 0) + { + if (sRslt[sRslt.Length - 1] != '\\' && sRslt[sRslt.Length - 1] != '/') + { + sRslt += "/"; + } + } + + return sRslt; + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Clone() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + Rectangle rect = new Rectangle(0, 0, 50, 50); + using (Bitmap bmp = new Bitmap(sInFile)) + using (Bitmap bmpNew = bmp.Clone(rect, PixelFormat.Format32bppArgb)) + { + Color colororg0 = bmp.GetPixel(0, 0); + Color colororg50 = bmp.GetPixel(49, 49); + Color colornew0 = bmpNew.GetPixel(0, 0); + Color colornew50 = bmpNew.GetPixel(49, 49); + + Assert.Equal(colororg0, colornew0); + Assert.Equal(colororg50, colornew50); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CloneImage() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + using (Bitmap bmpNew = (Bitmap)bmp.Clone()) + { + Assert.Equal(bmp.Width, bmpNew.Width); + Assert.Equal(bmp.Height, bmpNew.Height); + Assert.Equal(bmp.PixelFormat, bmpNew.PixelFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Frames() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + int cnt = bmp.GetFrameCount(FrameDimension.Page); + int active = bmp.SelectActiveFrame(FrameDimension.Page, 0); + + Assert.Equal(1, cnt); + Assert.Equal(0, active); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void FileDoesNotExists() + { + Assert.Throws(() => new Bitmap("FileDoesNotExists.jpg")); + } + + static string ByteArrayToString(byte[] arrInput) + { + StringBuilder sOutput = new StringBuilder(arrInput.Length); + for (int i = 0; i < arrInput.Length; i++) + { + sOutput.Append(arrInput[i].ToString("X2")); + } + return sOutput.ToString(); + } + + + public string RotateBmp(Bitmap src, RotateFlipType rotate) + { + int width = 150, height = 150, index = 0; + byte[] pixels = new byte[width * height * 3]; + byte[] hash; + Color clr; + + using (Bitmap bmp_rotate = src.Clone(new RectangleF(0, 0, width, height), PixelFormat.Format32bppArgb)) + { + bmp_rotate.RotateFlip(rotate); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + clr = bmp_rotate.GetPixel(x, y); + pixels[index++] = clr.R; + pixels[index++] = clr.G; + pixels[index++] = clr.B; + } + } + + hash = MD5.Create().ComputeHash(pixels); + return ByteArrayToString(hash); + } + } + public string RotateIndexedBmp(Bitmap src, RotateFlipType type) + { + int pixels_per_byte; + + switch (src.PixelFormat) + { + case PixelFormat.Format1bppIndexed: + pixels_per_byte = 8; + break; + case PixelFormat.Format4bppIndexed: + pixels_per_byte = 2; + break; + case PixelFormat.Format8bppIndexed: + pixels_per_byte = 1; + break; + + default: + throw new Exception("Cannot pass a bitmap of format " + src.PixelFormat + " to RotateIndexedBmp"); + } + + using (Bitmap test = src.Clone() as Bitmap) + { + test.RotateFlip(type); + + BitmapData data = null; + byte[] pixel_data; + + try + { + data = test.LockBits(new Rectangle(0, 0, test.Width, test.Height), ImageLockMode.ReadOnly, test.PixelFormat); + + int scan_size = (data.Width + pixels_per_byte - 1) / pixels_per_byte; + pixel_data = new byte[data.Height * scan_size]; + + for (int y = 0; y < data.Height; y++) + { + IntPtr src_ptr = (IntPtr)(y * data.Stride + data.Scan0.ToInt64()); + int dest_offset = y * scan_size; + for (int x = 0; x < scan_size; x++) + pixel_data[dest_offset + x] = Marshal.ReadByte(src_ptr, x); + } + } + finally + { + if (test != null && data != null) + { + try + { test.UnlockBits(data); } + catch { } + } + } + + if (pixel_data == null) + return "--ERROR--"; + + byte[] hash = MD5.Create().ComputeHash(pixel_data); + return ByteArrayToString(hash); + } + } + + + // Rotate bitmap in diffent ways, and check the result + // pixels using MD5 + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Rotate() + { + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bmp = new Bitmap(sInFile)) + { + Assert.Equal("312958A3C67402E1299413794988A3C7", RotateBmp(bmp, RotateFlipType.Rotate90FlipNone)); + Assert.Equal("BF70D8DA4F1545AEDD77D0296B47AE58", RotateBmp(bmp, RotateFlipType.Rotate180FlipNone)); + Assert.Equal("15AD2ADBDC7090C0EC744D0F7ACE2FD4", RotateBmp(bmp, RotateFlipType.Rotate270FlipNone)); + Assert.Equal("2E10FEC1F4FD64ECC51D7CE68AEB183F", RotateBmp(bmp, RotateFlipType.RotateNoneFlipX)); + Assert.Equal("E63204779B566ED01162B90B49BD9EE9", RotateBmp(bmp, RotateFlipType.Rotate90FlipX)); + Assert.Equal("B1ECB17B5093E13D04FF55CFCF7763C9", RotateBmp(bmp, RotateFlipType.Rotate180FlipX)); + Assert.Equal("71A173882C16755D86F4BC2653237425", RotateBmp(bmp, RotateFlipType.Rotate270FlipX)); + } + } + + private Bitmap CreateBitmap(int width, int height, PixelFormat fmt) + { + Bitmap bmp = new Bitmap(width, height, fmt); + using (Graphics gr = Graphics.FromImage(bmp)) + { + Color c = Color.FromArgb(255, 100, 200, 250); + for (int x = 1; x < 80; x++) + { + bmp.SetPixel(x, 1, c); + bmp.SetPixel(x, 2, c); + bmp.SetPixel(x, 78, c); + bmp.SetPixel(x, 79, c); + } + for (int y = 3; y < 78; y++) + { + bmp.SetPixel(1, y, c); + bmp.SetPixel(2, y, c); + bmp.SetPixel(78, y, c); + bmp.SetPixel(79, y, c); + } + } + return bmp; + } + + private byte[] HashPixels(Bitmap bmp) + { + int len = bmp.Width * bmp.Height * 4; + int index = 0; + byte[] pixels = new byte[len]; + + for (int y = 0; y < bmp.Height; y++) + { + for (int x = 0; x < bmp.Width; x++) + { + Color clr = bmp.GetPixel(x, y); + pixels[index++] = clr.R; + pixels[index++] = clr.G; + pixels[index++] = clr.B; + } + } + return MD5.Create().ComputeHash(pixels); + } + + private byte[] HashLock(Bitmap bmp, int width, int height, PixelFormat fmt, ImageLockMode mode) + { + int len = bmp.Width * bmp.Height * 4; + byte[] pixels = new byte[len]; + BitmapData bd = bmp.LockBits(new Rectangle(0, 0, width, height), mode, fmt); + try + { + int index = 0; + int bbps = Image.GetPixelFormatSize(fmt); + long pos = bd.Scan0.ToInt64(); + byte[] btv = new byte[1]; + for (int y = 0; y < bd.Height; y++) + { + for (int x = 0; x < bd.Width; x++) + { + /* Read the pixels*/ + for (int bt = 0; bt < bbps / 8; bt++, index++) + { + long cur = pos; + cur += y * bd.Stride; + cur += x * bbps / 8; + cur += bt; + Marshal.Copy((IntPtr)cur, btv, 0, 1); + pixels[index] = btv[0]; + + /* Make change of all the colours = 250 to 10*/ + if (btv[0] == 250) + { + btv[0] = 10; + Marshal.Copy(btv, 0, (IntPtr)cur, 1); + } + } + } + } + + for (int i = index; i < len; i++) + pixels[index] = 0; + } + finally + { + bmp.UnlockBits(bd); + } + return MD5.Create().ComputeHash(pixels); + } + + // Tests the LockBitmap functions. Makes a hash of the block of pixels that it returns + // firsts, changes them, and then using GetPixel does another check of the changes. + // The results match the .NET Framework + private static byte[] DefaultBitmapHash = new byte[] { 0xD8, 0xD3, 0x68, 0x9C, 0x86, 0x7F, 0xB6, 0xA0, 0x76, 0xD6, 0x00, 0xEF, 0xFF, 0xE5, 0x8E, 0x1B }; + private static byte[] FinalWholeBitmapHash = new byte[] { 0x5F, 0x52, 0x98, 0x37, 0xE3, 0x94, 0xE1, 0xA6, 0x06, 0x6C, 0x5B, 0xF1, 0xA9, 0xC2, 0xA9, 0x43 }; + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format32bppArgb_ReadWrite_Whole() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x89, 0x6A, 0x6B, 0x35, 0x5C, 0x89, 0xD9, 0xE9, 0xF4, 0x51, 0xD5, 0x89, 0xED, 0x28, 0x68, 0x5C }; + byte[] actual = HashLock(bmp, bmp.Width, bmp.Height, PixelFormat.Format32bppArgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalWholeBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format32bppPArgb_ReadWrite_Whole() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x89, 0x6A, 0x6B, 0x35, 0x5C, 0x89, 0xD9, 0xE9, 0xF4, 0x51, 0xD5, 0x89, 0xED, 0x28, 0x68, 0x5C }; + byte[] actual = HashLock(bmp, bmp.Width, bmp.Height, PixelFormat.Format32bppPArgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalWholeBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221")] + public void LockBitmap_Format32bppArgb_Format32bppRgb_ReadWrite_Whole() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0xC0, 0x28, 0xB5, 0x2E, 0x86, 0x90, 0x6F, 0x37, 0x09, 0x5F, 0x49, 0xA4, 0x91, 0xDA, 0xEE, 0xB9 }; + byte[] actual = HashLock(bmp, bmp.Width, bmp.Height, PixelFormat.Format32bppRgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalWholeBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format24bppRgb_ReadWrite_Whole() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0xA7, 0xB2, 0x50, 0x04, 0x11, 0x12, 0x64, 0x68, 0x6B, 0x7D, 0x2F, 0x6E, 0x69, 0x24, 0xCB, 0x14 }; + byte[] actual = HashLock(bmp, bmp.Width, bmp.Height, PixelFormat.Format24bppRgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalWholeBitmapHash, HashPixels(bmp)); + } + } + + private static byte[] FinalPartialBitmapHash = new byte[] { 0xED, 0xD8, 0xDC, 0x9B, 0x44, 0x00, 0x22, 0x9B, 0x07, 0x06, 0x4A, 0x21, 0x70, 0xA7, 0x31, 0x1D }; + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format32bppArgb_ReadWrite_Partial() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x5D, 0xFF, 0x02, 0x34, 0xEB, 0x7C, 0xF7, 0x42, 0xD4, 0xB7, 0x70, 0x49, 0xB4, 0x06, 0x79, 0xBC }; + byte[] actual = HashLock(bmp, 50, 50, PixelFormat.Format32bppArgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalPartialBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format32bppPArgb_ReadWrite_Partial() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x5D, 0xFF, 0x02, 0x34, 0xEB, 0x7C, 0xF7, 0x42, 0xD4, 0xB7, 0x70, 0x49, 0xB4, 0x06, 0x79, 0xBC }; + byte[] actual = HashLock(bmp, 50, 50, PixelFormat.Format32bppPArgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalPartialBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/22221")] + public void LockBitmap_Format32bppArgb_Format32bppRgb_ReadWrite_Partial() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x72, 0x33, 0x09, 0x67, 0x53, 0x65, 0x38, 0xF9, 0xE4, 0x58, 0xE1, 0x0A, 0xAA, 0x6A, 0xCC, 0xB8 }; + byte[] actual = HashLock(bmp, 50, 50, PixelFormat.Format32bppRgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalPartialBitmapHash, HashPixels(bmp)); + } + } + + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockBitmap_Format32bppArgb_Format24bppRgb_ReadWrite_Partial() + { + using (Bitmap bmp = CreateBitmap(100, 100, PixelFormat.Format32bppArgb)) + { + Assert.Equal(DefaultBitmapHash, HashPixels(bmp)); + byte[] expected = { 0x4D, 0x39, 0x21, 0x88, 0xC2, 0x17, 0x14, 0x5F, 0x89, 0x9E, 0x02, 0x75, 0xF3, 0x64, 0xD8, 0xF0 }; + byte[] actual = HashLock(bmp, 50, 50, PixelFormat.Format24bppRgb, ImageLockMode.ReadWrite); + Assert.Equal(expected, actual); + Assert.Equal(FinalPartialBitmapHash, HashPixels(bmp)); + } + } + + // Tests the LockBitmap and UnlockBitmap functions, specifically the copying + // of bitmap data in the directions indicated by the ImageLockMode. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported), nameof(PlatformDetection.IsNotArm64Process))] // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + public void LockUnlockBitmap() + { + BitmapData data; + int pixel_value; + Color pixel_colour; + + Color red = Color.FromArgb(Color.Red.A, Color.Red.R, Color.Red.G, Color.Red.B); + Color blue = Color.FromArgb(Color.Blue.A, Color.Blue.R, Color.Blue.G, Color.Blue.B); + + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format32bppRgb)) + { + bmp.SetPixel(0, 0, red); + pixel_colour = bmp.GetPixel(0, 0); + Assert.Equal(red, pixel_colour); + + data = bmp.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); + try + { + pixel_value = Marshal.ReadByte(data.Scan0, 0); + pixel_value |= Marshal.ReadByte(data.Scan0, 1) << 8; + pixel_value |= Marshal.ReadByte(data.Scan0, 2) << 16; + pixel_value |= Marshal.ReadByte(data.Scan0, 3) << 24; + + pixel_colour = Color.FromArgb(pixel_value); + // Disregard alpha information in the test + pixel_colour = Color.FromArgb(red.A, pixel_colour.R, pixel_colour.G, pixel_colour.B); + Assert.Equal(red, pixel_colour); + + // write blue but we're locked in read-only... + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + Marshal.WriteByte(data.Scan0, 3, blue.A); + } + finally + { + bmp.UnlockBits(data); + pixel_colour = bmp.GetPixel(0, 0); + // Disregard alpha information in the test + pixel_colour = Color.FromArgb(red.A, pixel_colour.R, pixel_colour.G, pixel_colour.B); + // ...so we still read red after unlocking + Assert.Equal(red, pixel_colour); + } + + data = bmp.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + try + { + // write blue + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + Marshal.WriteByte(data.Scan0, 3, blue.A); + } + finally + { + bmp.UnlockBits(data); + pixel_colour = bmp.GetPixel(0, 0); + // Disregard alpha information in the test + pixel_colour = Color.FromArgb(blue.A, pixel_colour.R, pixel_colour.G, pixel_colour.B); + // read blue + Assert.Equal(blue, pixel_colour); + } + } + + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format32bppArgb)) + { + bmp.SetPixel(0, 0, red); + + data = bmp.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); + try + { + byte b = Marshal.ReadByte(data.Scan0, 0); + byte g = Marshal.ReadByte(data.Scan0, 1); + byte r = Marshal.ReadByte(data.Scan0, 2); + pixel_colour = Color.FromArgb(red.A, r, g, b); + Assert.Equal(red, pixel_colour); + // write blue but we're locked in read-only... + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + } + finally + { + bmp.UnlockBits(data); + // ...so we still read red after unlocking + Assert.Equal(red, bmp.GetPixel(0, 0)); + } + + data = bmp.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + try + { + // write blue + Marshal.WriteByte(data.Scan0, 0, blue.B); + Marshal.WriteByte(data.Scan0, 1, blue.G); + Marshal.WriteByte(data.Scan0, 2, blue.R); + } + finally + { + bmp.UnlockBits(data); + // read blue + Assert.Equal(blue, bmp.GetPixel(0, 0)); + } + } + } + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DefaultFormat1() + { + using (Bitmap bmp = new Bitmap(20, 20)) + { + Assert.Equal(ImageFormat.MemoryBmp, bmp.RawFormat); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void DefaultFormat2() + { + string filename = Path.GetTempFileName(); + using (Bitmap bmp = new Bitmap(20, 20)) + { + bmp.Save(filename); + } + + using (Bitmap other = new Bitmap(filename)) + { + Assert.Equal(ImageFormat.Png, other.RawFormat); + } + File.Delete(filename); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void BmpDataStride1() + { + Bitmap bmp = new Bitmap(184, 184, PixelFormat.Format1bppIndexed); + BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed); + try + { + Assert.Equal(24, data.Stride); + } + finally + { + bmp.UnlockBits(data); + bmp.Dispose(); + } + } + + static int[] palette1 = { + -16777216, + -1, + }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format1bppIndexed_Palette() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format1bppIndexed)) + { + ColorPalette pal = bmp.Palette; + Assert.Equal(2, pal.Entries.Length); + for (int i = 0; i < pal.Entries.Length; i++) + { + Assert.Equal(palette1[i], pal.Entries[i].ToArgb()); + } + Assert.Equal(2, pal.Flags); + } + } + + static int[] palette16 = { + -16777216, + -8388608, + -16744448, + -8355840, + -16777088, + -8388480, + -16744320, + -8355712, + -4144960, + -65536, + -16711936, + -256, + -16776961, + -65281, + -16711681, + -1, + }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format4bppIndexed_Palette() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format4bppIndexed)) + { + ColorPalette pal = bmp.Palette; + Assert.Equal(16, pal.Entries.Length); + for (int i = 0; i < pal.Entries.Length; i++) + { + Assert.Equal(palette16[i], pal.Entries[i].ToArgb()); + } + Assert.Equal(0, pal.Flags); + } + } + + static int[] palette256 = { + -16777216, + -8388608, + -16744448, + -8355840, + -16777088, + -8388480, + -16744320, + -8355712, + -4144960, + -65536, + -16711936, + -256, + -16776961, + -65281, + -16711681, + -1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + -16777216, + -16777165, + -16777114, + -16777063, + -16777012, + -16776961, + -16764160, + -16764109, + -16764058, + -16764007, + -16763956, + -16763905, + -16751104, + -16751053, + -16751002, + -16750951, + -16750900, + -16750849, + -16738048, + -16737997, + -16737946, + -16737895, + -16737844, + -16737793, + -16724992, + -16724941, + -16724890, + -16724839, + -16724788, + -16724737, + -16711936, + -16711885, + -16711834, + -16711783, + -16711732, + -16711681, + -13434880, + -13434829, + -13434778, + -13434727, + -13434676, + -13434625, + -13421824, + -13421773, + -13421722, + -13421671, + -13421620, + -13421569, + -13408768, + -13408717, + -13408666, + -13408615, + -13408564, + -13408513, + -13395712, + -13395661, + -13395610, + -13395559, + -13395508, + -13395457, + -13382656, + -13382605, + -13382554, + -13382503, + -13382452, + -13382401, + -13369600, + -13369549, + -13369498, + -13369447, + -13369396, + -13369345, + -10092544, + -10092493, + -10092442, + -10092391, + -10092340, + -10092289, + -10079488, + -10079437, + -10079386, + -10079335, + -10079284, + -10079233, + -10066432, + -10066381, + -10066330, + -10066279, + -10066228, + -10066177, + -10053376, + -10053325, + -10053274, + -10053223, + -10053172, + -10053121, + -10040320, + -10040269, + -10040218, + -10040167, + -10040116, + -10040065, + -10027264, + -10027213, + -10027162, + -10027111, + -10027060, + -10027009, + -6750208, + -6750157, + -6750106, + -6750055, + -6750004, + -6749953, + -6737152, + -6737101, + -6737050, + -6736999, + -6736948, + -6736897, + -6724096, + -6724045, + -6723994, + -6723943, + -6723892, + -6723841, + -6711040, + -6710989, + -6710938, + -6710887, + -6710836, + -6710785, + -6697984, + -6697933, + -6697882, + -6697831, + -6697780, + -6697729, + -6684928, + -6684877, + -6684826, + -6684775, + -6684724, + -6684673, + -3407872, + -3407821, + -3407770, + -3407719, + -3407668, + -3407617, + -3394816, + -3394765, + -3394714, + -3394663, + -3394612, + -3394561, + -3381760, + -3381709, + -3381658, + -3381607, + -3381556, + -3381505, + -3368704, + -3368653, + -3368602, + -3368551, + -3368500, + -3368449, + -3355648, + -3355597, + -3355546, + -3355495, + -3355444, + -3355393, + -3342592, + -3342541, + -3342490, + -3342439, + -3342388, + -3342337, + -65536, + -65485, + -65434, + -65383, + -65332, + -65281, + -52480, + -52429, + -52378, + -52327, + -52276, + -52225, + -39424, + -39373, + -39322, + -39271, + -39220, + -39169, + -26368, + -26317, + -26266, + -26215, + -26164, + -26113, + -13312, + -13261, + -13210, + -13159, + -13108, + -13057, + -256, + -205, + -154, + -103, + -52, + -1, + }; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Format8bppIndexed_Palette() + { + using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed)) + { + ColorPalette pal = bmp.Palette; + Assert.Equal(256, pal.Entries.Length); + for (int i = 0; i < pal.Entries.Length; i++) + { + Assert.Equal(palette256[i], pal.Entries[i].ToArgb()); + } + Assert.Equal(4, pal.Flags); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void XmlSerialization() + { + new XmlSerializer(typeof(Bitmap)); + } + + private void SetResolution(float x, float y) + { + using (Bitmap bmp = new Bitmap(1, 1)) + { + bmp.SetResolution(x, y); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_Zero() + { + Assert.Throws(() => SetResolution(0.0f, 0.0f)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_Negative_X() + { + Assert.Throws(() => SetResolution(-1.0f, 1.0f)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_Negative_Y() + { + Assert.Throws(() => SetResolution(1.0f, -1.0f)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_MaxValue() + { + SetResolution(float.MaxValue, float.MaxValue); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_PositiveInfinity() + { + SetResolution(float.PositiveInfinity, float.PositiveInfinity); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_NaN() + { + Assert.Throws(() => SetResolution(float.NaN, float.NaN)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void SetResolution_NegativeInfinity() + { + Assert.Throws(() => SetResolution(float.NegativeInfinity, float.NegativeInfinity)); + } + } + + public class BitmapFullTrustTest + { + [ConditionalFact(Helpers.IsDrawingSupported)] + public void BitmapIntIntIntPixelFormatIntPtrCtor() + { + new Bitmap(1, 1, 1, PixelFormat.Format1bppIndexed, IntPtr.Zero); + } + + // BitmapFromHicon## is *almost* the same as IconTest.Icon##ToBitmap except + // for the Flags property + + private void HiconTest(string msg, Bitmap b, int size) + { + Assert.Equal(PixelFormat.Format32bppArgb, b.PixelFormat); + // unlike the GDI+ icon decoder the palette isn't kept + Assert.Equal(0, b.Palette.Entries.Length); + Assert.Equal(size, b.Height); + Assert.Equal(size, b.Width); + Assert.Equal(b.RawFormat, ImageFormat.MemoryBmp); + Assert.Equal(335888, b.Flags); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Hicon16() + { + IntPtr hicon; + int size; + using (Icon icon = new Icon(Helpers.GetTestBitmapPath("16x16_one_entry_4bit.ico"))) + { + size = icon.Width; + using (Bitmap bitmap = Bitmap.FromHicon(icon.Handle)) + { + HiconTest("Icon.Handle/FromHicon", bitmap, size); + hicon = bitmap.GetHicon(); + } + } + using (Bitmap bitmap2 = Bitmap.FromHicon(hicon)) + { + // hicon survives bitmap and icon disposal + HiconTest("GetHicon/FromHicon", bitmap2, size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Hicon32() + { + IntPtr hicon; + int size; + using (Icon icon = new Icon(Helpers.GetTestBitmapPath("32x32_one_entry_4bit.ico"))) + { + size = icon.Width; + using (Bitmap bitmap = Bitmap.FromHicon(icon.Handle)) + { + HiconTest("Icon.Handle/FromHicon", bitmap, size); + hicon = bitmap.GetHicon(); + } + } + using (Bitmap bitmap2 = Bitmap.FromHicon(hicon)) + { + // hicon survives bitmap and icon disposal + HiconTest("GetHicon/FromHicon", bitmap2, size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Hicon64() + { + IntPtr hicon; + int size; + using (Icon icon = new Icon(Helpers.GetTestBitmapPath("64x64_one_entry_8bit.ico"))) + { + size = icon.Width; + using (Bitmap bitmap = Bitmap.FromHicon(icon.Handle)) + { + HiconTest("Icon.Handle/FromHicon", bitmap, size); + hicon = bitmap.GetHicon(); + } + } + using (Bitmap bitmap2 = Bitmap.FromHicon(hicon)) + { + // hicon survives bitmap and icon disposal + HiconTest("GetHicon/FromHicon", bitmap2, size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Hicon96() + { + IntPtr hicon; + int size; + using (Icon icon = new Icon(Helpers.GetTestBitmapPath("96x96_one_entry_8bit.ico"))) + { + size = icon.Width; + using (Bitmap bitmap = Bitmap.FromHicon(icon.Handle)) + { + HiconTest("Icon.Handle/FromHicon", bitmap, size); + hicon = bitmap.GetHicon(); + } + } + using (Bitmap bitmap2 = Bitmap.FromHicon(hicon)) + { + // hicon survives bitmap and icon disposal + HiconTest("GetHicon/FromHicon", bitmap2, size); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void HBitmap() + { + IntPtr hbitmap; + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bitmap = new Bitmap(sInFile)) + { + Assert.Equal(PixelFormat.Format24bppRgb, bitmap.PixelFormat); + Assert.Equal(0, bitmap.Palette.Entries.Length); + Assert.Equal(183, bitmap.Height); + Assert.Equal(173, bitmap.Width); + Assert.Equal(73744, bitmap.Flags); + Assert.Equal(bitmap.RawFormat, ImageFormat.Bmp); + hbitmap = bitmap.GetHbitmap(); + } + + // hbitmap survives original bitmap disposal + using (Image image = Image.FromHbitmap(hbitmap)) + { + //Assert.Equal (PixelFormat.Format32bppRgb, image.PixelFormat); + Assert.Equal(0, image.Palette.Entries.Length); + Assert.Equal(183, image.Height); + Assert.Equal(173, image.Width); + Assert.Equal(335888, image.Flags); + Assert.Equal(image.RawFormat, ImageFormat.MemoryBmp); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void CreateMultipleBitmapFromSameHBITMAP() + { + IntPtr hbitmap; + string sInFile = Helpers.GetTestBitmapPath("almogaver24bits.bmp"); + using (Bitmap bitmap = new Bitmap(sInFile)) + { + Assert.Equal(PixelFormat.Format24bppRgb, bitmap.PixelFormat); + Assert.Equal(0, bitmap.Palette.Entries.Length); + Assert.Equal(183, bitmap.Height); + Assert.Equal(173, bitmap.Width); + Assert.Equal(73744, bitmap.Flags); + Assert.Equal(bitmap.RawFormat, ImageFormat.Bmp); + hbitmap = bitmap.GetHbitmap(); + } + // hbitmap survives original bitmap disposal + using (Image image = Image.FromHbitmap(hbitmap)) + { + //Assert.Equal (PixelFormat.Format32bppRgb, image.PixelFormat); + Assert.Equal(0, image.Palette.Entries.Length); + Assert.Equal(183, image.Height); + Assert.Equal(173, image.Width); + Assert.Equal(335888, image.Flags); + Assert.Equal(image.RawFormat, ImageFormat.MemoryBmp); + } + using (Image image2 = Image.FromHbitmap(hbitmap)) + { + //Assert.Equal (PixelFormat.Format32bppRgb, image2.PixelFormat); + Assert.Equal(0, image2.Palette.Entries.Length); + Assert.Equal(183, image2.Height); + Assert.Equal(173, image2.Width); + Assert.Equal(335888, image2.Flags); + Assert.Equal(image2.RawFormat, ImageFormat.MemoryBmp); + } + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs b/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs new file mode 100644 index 00000000000..e9253c60286 --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Drawing/GraphicsTests.cs @@ -0,0 +1,3219 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Graphics class testing unit +// +// Authors: +// Jordi Mas, jordi@ximian.com +// Sebastien Pouliot +// +// Copyright (C) 2005-2008 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Drawing.Text; +using Microsoft.DotNet.XUnitExtensions; +using Xunit; + +namespace MonoTests.System.Drawing +{ + [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsDrawingSupported))] + public class GraphicsTest : IDisposable + { + private RectangleF[] rects; + private Font font; + + public GraphicsTest() + { + try + { + font = new Font("Arial", 12); + } + catch + { + } + } + + public void Dispose() + { + if (font != null) + font.Dispose(); + } + + private bool IsEmptyBitmap(Bitmap bitmap, out int x, out int y) + { + bool result = true; + int empty = Color.Empty.ToArgb(); + for (y = 0; y < bitmap.Height; y++) + { + for (x = 0; x < bitmap.Width; x++) + { + if (bitmap.GetPixel(x, y).ToArgb() != empty) + return false; + } + } + + x = -1; + y = -1; + return result; + } + + private void CheckForEmptyBitmap(Bitmap bitmap) + { + int x, y; + if (!IsEmptyBitmap(bitmap, out x, out y)) + Assert.True(false, string.Format("Position {0},{1}", x, y)); + } + + private void CheckForNonEmptyBitmap(Bitmap bitmap) + { + int x, y; + if (IsEmptyBitmap(bitmap, out x, out y)) + Assert.True(false); + } + + private void AssertEquals(string msg, object expected, object actual) + { + Assert.Equal(expected, actual); + } + + private void AssertEquals(string msg, double expected, double actual, int precision) + { + Assert.Equal(expected, actual, precision); + } + + [Fact] + public void DefaultProperties() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + using (Region r = new Region()) + { + Assert.Equal(r.GetBounds(g), g.ClipBounds); + Assert.Equal(CompositingMode.SourceOver, g.CompositingMode); + Assert.Equal(CompositingQuality.Default, g.CompositingQuality); + Assert.Equal(InterpolationMode.Bilinear, g.InterpolationMode); + Assert.Equal(1, g.PageScale); + Assert.Equal(GraphicsUnit.Display, g.PageUnit); + Assert.Equal(PixelOffsetMode.Default, g.PixelOffsetMode); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + Assert.Equal(SmoothingMode.None, g.SmoothingMode); + Assert.Equal(TextRenderingHint.SystemDefault, g.TextRenderingHint); + } + } + + [Fact] + public void SetGetProperties() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.GammaCorrected; + g.InterpolationMode = InterpolationMode.HighQualityBilinear; + g.PageScale = 2; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.RenderingOrigin = new Point(10, 20); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextRenderingHint = TextRenderingHint.SystemDefault; + + //Clipping set/get tested in clipping functions + Assert.Equal(CompositingMode.SourceCopy, g.CompositingMode); + Assert.Equal(CompositingQuality.GammaCorrected, g.CompositingQuality); + Assert.Equal(InterpolationMode.HighQualityBilinear, g.InterpolationMode); + Assert.Equal(2, g.PageScale); + Assert.Equal(GraphicsUnit.Inch, g.PageUnit); + Assert.Equal(PixelOffsetMode.Half, g.PixelOffsetMode); + Assert.Equal(new Point(10, 20), g.RenderingOrigin); + Assert.Equal(SmoothingMode.AntiAlias, g.SmoothingMode); + Assert.Equal(TextRenderingHint.SystemDefault, g.TextRenderingHint); + } + } + + // Properties + [Fact] + public void Clip() + { + RectangleF[] rects; + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clip = new Region(new Rectangle(50, 40, 210, 220)); + rects = g.Clip.GetRegionScans(new Matrix()); + + Assert.Equal(1, rects.Length); + Assert.Equal(50, rects[0].X); + Assert.Equal(40, rects[0].Y); + Assert.Equal(210, rects[0].Width); + Assert.Equal(220, rects[0].Height); + } + } + + [Fact] + public void Clip_NotAReference() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.True(g.Clip.IsInfinite(g)); + g.Clip.IsEmpty(g); + Assert.False(g.Clip.IsEmpty(g)); + Assert.True(g.Clip.IsInfinite(g)); + } + } + + [Fact] + public void ExcludeClip() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clip = new Region(new RectangleF(10, 10, 100, 100)); + g.ExcludeClip(new Rectangle(40, 60, 100, 20)); + rects = g.Clip.GetRegionScans(new Matrix()); + + Assert.Equal(3, rects.Length); + + Assert.Equal(10, rects[0].X); + Assert.Equal(10, rects[0].Y); + Assert.Equal(100, rects[0].Width); + Assert.Equal(50, rects[0].Height); + + Assert.Equal(10, rects[1].X); + Assert.Equal(60, rects[1].Y); + Assert.Equal(30, rects[1].Width); + Assert.Equal(20, rects[1].Height); + + Assert.Equal(10, rects[2].X); + Assert.Equal(80, rects[2].Y); + Assert.Equal(100, rects[2].Width); + Assert.Equal(30, rects[2].Height); + } + } + + [Fact] + public void IntersectClip() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clip = new Region(new RectangleF(260, 30, 60, 80)); + g.IntersectClip(new Rectangle(290, 40, 60, 80)); + rects = g.Clip.GetRegionScans(new Matrix()); + + Assert.Equal(1, rects.Length); + + Assert.Equal(290, rects[0].X); + Assert.Equal(40, rects[0].Y); + Assert.Equal(30, rects[0].Width); + Assert.Equal(70, rects[0].Height); + } + } + + [Fact] + public void ResetClip() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.Clip = new Region(new RectangleF(260, 30, 60, 80)); + g.IntersectClip(new Rectangle(290, 40, 60, 80)); + g.ResetClip(); + rects = g.Clip.GetRegionScans(new Matrix()); + + Assert.Equal(1, rects.Length); + + Assert.Equal(-4194304, rects[0].X); + Assert.Equal(-4194304, rects[0].Y); + Assert.Equal(8388608, rects[0].Width); + Assert.Equal(8388608, rects[0].Height); + } + } + + [Fact] + public void SetClip() + { + RectangleF[] rects; + using (Bitmap bmp = new Bitmap(200, 200)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + // Region + g.SetClip(new Region(new Rectangle(50, 40, 210, 220)), CombineMode.Replace); + rects = g.Clip.GetRegionScans(new Matrix()); + Assert.Equal(1, rects.Length); + Assert.Equal(50, rects[0].X); + Assert.Equal(40, rects[0].Y); + Assert.Equal(210, rects[0].Width); + Assert.Equal(220, rects[0].Height); + } + + // RectangleF + using (Graphics g = Graphics.FromImage(bmp)) + { + g.SetClip(new RectangleF(50, 40, 210, 220)); + rects = g.Clip.GetRegionScans(new Matrix()); + Assert.Equal(1, rects.Length); + Assert.Equal(50, rects[0].X); + Assert.Equal(40, rects[0].Y); + Assert.Equal(210, rects[0].Width); + Assert.Equal(220, rects[0].Height); + } + + // Rectangle + using (Graphics g = Graphics.FromImage(bmp)) + { + g.SetClip(new Rectangle(50, 40, 210, 220)); + rects = g.Clip.GetRegionScans(new Matrix()); + Assert.Equal(1, rects.Length); + Assert.Equal(50, rects[0].X); + Assert.Equal(40, rects[0].Y); + Assert.Equal(210, rects[0].Width); + Assert.Equal(220, rects[0].Height); + } + } + } + + [Fact] + public void SetSaveReset() + { + using (Bitmap bmp = new Bitmap(200, 200)) + using (Graphics g = Graphics.FromImage(bmp)) + { + GraphicsState state_default, state_modified; + + state_default = g.Save(); // Default + + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.GammaCorrected; + g.InterpolationMode = InterpolationMode.HighQualityBilinear; + g.PageScale = 2; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.Clip = new Region(new Rectangle(0, 0, 100, 100)); + g.RenderingOrigin = new Point(10, 20); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextRenderingHint = TextRenderingHint.ClearTypeGridFit; + + + state_modified = g.Save(); // Modified + + g.CompositingMode = CompositingMode.SourceOver; + g.CompositingQuality = CompositingQuality.Default; + g.InterpolationMode = InterpolationMode.Bilinear; + g.PageScale = 5; + g.PageUnit = GraphicsUnit.Display; + g.PixelOffsetMode = PixelOffsetMode.Default; + g.Clip = new Region(new Rectangle(1, 2, 20, 25)); + g.RenderingOrigin = new Point(5, 6); + g.SmoothingMode = SmoothingMode.None; + g.TextRenderingHint = TextRenderingHint.SystemDefault; + + g.Restore(state_modified); + + Assert.Equal(CompositingMode.SourceCopy, g.CompositingMode); + Assert.Equal(CompositingQuality.GammaCorrected, g.CompositingQuality); + Assert.Equal(InterpolationMode.HighQualityBilinear, g.InterpolationMode); + Assert.Equal(2, g.PageScale); + Assert.Equal(GraphicsUnit.Inch, g.PageUnit); + Assert.Equal(PixelOffsetMode.Half, g.PixelOffsetMode); + Assert.Equal(new Point(10, 20), g.RenderingOrigin); + Assert.Equal(SmoothingMode.AntiAlias, g.SmoothingMode); + Assert.Equal(TextRenderingHint.ClearTypeGridFit, g.TextRenderingHint); + Assert.Equal(0, (int)g.ClipBounds.X); + Assert.Equal(0, (int)g.ClipBounds.Y); + + g.Restore(state_default); + + Assert.Equal(CompositingMode.SourceOver, g.CompositingMode); + Assert.Equal(CompositingQuality.Default, g.CompositingQuality); + Assert.Equal(InterpolationMode.Bilinear, g.InterpolationMode); + Assert.Equal(1, g.PageScale); + Assert.Equal(GraphicsUnit.Display, g.PageUnit); + Assert.Equal(PixelOffsetMode.Default, g.PixelOffsetMode); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + Assert.Equal(SmoothingMode.None, g.SmoothingMode); + Assert.Equal(TextRenderingHint.SystemDefault, g.TextRenderingHint); + + Region r = new Region(); + Assert.Equal(r.GetBounds(g), g.ClipBounds); + } + } + + [Fact] + public void LoadIndexed_BmpFile() + { + // Tests that we can load an indexed file, but... + string sInFile = Helpers.GetTestBitmapPath("almogaver1bit.bmp"); + // note: file is misnamed (it's a 4bpp bitmap) + using (Image img = Image.FromFile(sInFile)) + { + Assert.Equal(PixelFormat.Format4bppIndexed, img.PixelFormat); + Exception exception = AssertExtensions.Throws(() => Graphics.FromImage(img)); + if (exception is ArgumentException argumentException) + Assert.Equal("image", argumentException.ParamName); + } + } + + class BitmapAndGraphics : IDisposable + { + private readonly Bitmap _bitmap; + public Graphics Graphics { get; } + public BitmapAndGraphics(int width, int height) + { + _bitmap = new Bitmap(width, height); + Graphics = Graphics.FromImage(_bitmap); + Graphics.Clip = new Region(new Rectangle(0, 0, width, height)); + } + public void Dispose() { Graphics.Dispose(); _bitmap.Dispose(); } + } + + private void Compare(string msg, RectangleF b1, RectangleF b2) + { + AssertEquals(msg + ".compare.X", b1.X, b2.X); + AssertEquals(msg + ".compare.Y", b1.Y, b2.Y); + AssertEquals(msg + ".compare.Width", b1.Width, b2.Width); + AssertEquals(msg + ".compare.Height", b1.Height, b2.Height); + } + + [Fact] + public void Clip_GetBounds() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + RectangleF bounds = g.Clip.GetBounds(g); + Assert.Equal(0, bounds.X); + Assert.Equal(0, bounds.Y); + Assert.Equal(16, bounds.Width); + Assert.Equal(16, bounds.Height); + Assert.True(g.Transform.IsIdentity); + } + } + + [Fact] + public void Clip_TranslateTransform() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.TranslateTransform(12.22f, 10.10f); + RectangleF bounds = g.Clip.GetBounds(g); + Compare("translate", bounds, g.ClipBounds); + Assert.Equal(-12.2200003f, bounds.X); + Assert.Equal(-10.1000004f, bounds.Y); + Assert.Equal(16, bounds.Width); + Assert.Equal(16, bounds.Height); + float[] elements = g.Transform.Elements; + Assert.Equal(1, elements[0]); + Assert.Equal(0, elements[1]); + Assert.Equal(0, elements[2]); + Assert.Equal(1, elements[3]); + Assert.Equal(12.2200003f, elements[4]); + Assert.Equal(10.1000004f, elements[5]); + + g.ResetTransform(); + bounds = g.Clip.GetBounds(g); + Compare("reset", bounds, g.ClipBounds); + Assert.Equal(0, bounds.X); + Assert.Equal(0, bounds.Y); + Assert.Equal(16, bounds.Width); + Assert.Equal(16, bounds.Height); + Assert.True(g.Transform.IsIdentity); + } + } + + [Fact] + public void Transform_NonInvertibleMatrix() + { + using (Matrix matrix = new Matrix(123, 24, 82, 16, 47, 30)) + using (var b = new BitmapAndGraphics(16, 16)) + { + Assert.False(matrix.IsInvertible); + + var g = b.Graphics; + Assert.Throws(() => g.Transform = matrix); + } + } + + + [Fact] + public void Multiply_NonInvertibleMatrix() + { + using (Matrix matrix = new Matrix(123, 24, 82, 16, 47, 30)) + using (var b = new BitmapAndGraphics(16, 16)) + { + Assert.False(matrix.IsInvertible); + + var g = b.Graphics; + Assert.Throws(() => g.MultiplyTransform(matrix)); + } + } + + private void CheckBounds(string msg, RectangleF bounds, float x, float y, float w, float h) + { + AssertEquals(msg + ".X", x, bounds.X, 1); + AssertEquals(msg + ".Y", y, bounds.Y, 1); + AssertEquals(msg + ".Width", w, bounds.Width, 1); + AssertEquals(msg + ".Height", h, bounds.Height, 1); + } + + [Fact] + public void ClipBounds() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + CheckBounds("graphics.ClipBounds", g.ClipBounds, 0, 0, 16, 16); + CheckBounds("graphics.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 16); + + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + CheckBounds("clip.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("clip.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void ClipBounds_Rotate() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + g.RotateTransform(90); + CheckBounds("rotate.ClipBounds", g.ClipBounds, 0, -8, 8, 8); + CheckBounds("rotate.Clip.GetBounds", g.Clip.GetBounds(g), 0, -8, 8, 8); + + g.Transform = new Matrix(); + CheckBounds("identity.ClipBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + CheckBounds("identity.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void ClipBounds_Scale() + { + RectangleF clip = new Rectangle(0, 0, 8, 8); + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(clip); + g.ScaleTransform(0.25f, 0.5f); + CheckBounds("scale.ClipBounds", g.ClipBounds, 0, 0, 32, 16); + CheckBounds("scale.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 32, 16); + + g.SetClip(clip); + CheckBounds("setclip.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("setclip.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void ClipBounds_Translate() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + using (Region clone = g.Clip.Clone()) + { + g.TranslateTransform(8, 8); + CheckBounds("translate.ClipBounds", g.ClipBounds, -8, -8, 8, 8); + CheckBounds("translate.Clip.GetBounds", g.Clip.GetBounds(g), -8, -8, 8, 8); + + g.SetClip(clone, CombineMode.Replace); + CheckBounds("setclip.ClipBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + CheckBounds("setclip.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + } + + [Fact] + public void ClipBounds_Transform_Translation() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + g.Transform = new Matrix(1, 0, 0, 1, 8, 8); + CheckBounds("transform.ClipBounds", g.ClipBounds, -8, -8, 8, 8); + CheckBounds("transform.Clip.GetBounds", g.Clip.GetBounds(g), -8, -8, 8, 8); + + g.ResetTransform(); + CheckBounds("reset.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("reset.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void ClipBounds_Transform_Scale() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + g.Transform = new Matrix(0.5f, 0, 0, 0.25f, 0, 0); + CheckBounds("scale.ClipBounds", g.ClipBounds, 0, 0, 16, 32); + CheckBounds("scale.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 32); + + g.ResetClip(); + // see next test for ClipBounds + CheckBounds("resetclip.Clip.GetBounds", g.Clip.GetBounds(g), -4194304, -4194304, 8388608, 8388608); + Assert.True(g.Clip.IsInfinite(g)); + } + } + + [Fact] + public void ClipBounds_Multiply() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + g.Transform = new Matrix(1, 0, 0, 1, 8, 8); + g.MultiplyTransform(g.Transform); + CheckBounds("multiply.ClipBounds", g.ClipBounds, -16, -16, 8, 8); + CheckBounds("multiply.Clip.GetBounds", g.Clip.GetBounds(g), -16, -16, 8, 8); + + g.ResetTransform(); + CheckBounds("reset.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("reset.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void ClipBounds_Cumulative_Effects() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + CheckBounds("graphics.ClipBounds", g.ClipBounds, 0, 0, 16, 16); + CheckBounds("graphics.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 16); + + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + CheckBounds("clip.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("clip.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + + g.RotateTransform(90); + CheckBounds("rotate.ClipBounds", g.ClipBounds, 0, -8, 8, 8); + CheckBounds("rotate.Clip.GetBounds", g.Clip.GetBounds(g), 0, -8, 8, 8); + + g.ScaleTransform(0.25f, 0.5f); + CheckBounds("scale.ClipBounds", g.ClipBounds, 0, -16, 32, 16); + CheckBounds("scale.Clip.GetBounds", g.Clip.GetBounds(g), 0, -16, 32, 16); + + g.TranslateTransform(8, 8); + CheckBounds("translate.ClipBounds", g.ClipBounds, -8, -24, 32, 16); + CheckBounds("translate.Clip.GetBounds", g.Clip.GetBounds(g), -8, -24, 32, 16); + + g.MultiplyTransform(g.Transform); + CheckBounds("multiply.ClipBounds", g.ClipBounds, -104, -56, 64, 64); + CheckBounds("multiply.Clip.GetBounds", g.Clip.GetBounds(g), -104, -56, 64, 64); + + g.ResetTransform(); + CheckBounds("reset.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + CheckBounds("reset.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + } + } + + [Fact] + public void Clip_TranslateTransform_BoundsChange() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + CheckBounds("graphics.ClipBounds", g.ClipBounds, 0, 0, 16, 16); + CheckBounds("graphics.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 16); + g.TranslateTransform(-16, -16); + CheckBounds("translated.ClipBounds", g.ClipBounds, 16, 16, 16, 16); + CheckBounds("translated.Clip.GetBounds", g.Clip.GetBounds(g), 16, 16, 16, 16); + + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + // ClipBounds isn't affected by a previous translation + CheckBounds("rectangle.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + // Clip.GetBounds isn't affected by a previous translation + CheckBounds("rectangle.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + + g.ResetTransform(); + CheckBounds("reseted.ClipBounds", g.ClipBounds, -16, -16, 8, 8); + CheckBounds("reseted.Clip.GetBounds", g.Clip.GetBounds(g), -16, -16, 8, 8); + } + } + + [Fact] + public void Clip_RotateTransform_BoundsChange() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + CheckBounds("graphics.ClipBounds", g.ClipBounds, 0, 0, 16, 16); + CheckBounds("graphics.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 16); + // we select a "simple" angle because the region will be converted into + // a bitmap (well for libgdiplus) and we would lose precision after that + g.RotateTransform(90); + CheckBounds("rotated.ClipBounds", g.ClipBounds, 0, -16, 16, 16); + CheckBounds("rotated.Clip.GetBounds", g.Clip.GetBounds(g), 0, -16, 16, 16); + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + // ClipBounds isn't affected by a previous rotation (90) + CheckBounds("rectangle.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + // Clip.GetBounds isn't affected by a previous rotation + CheckBounds("rectangle.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + + g.ResetTransform(); + CheckBounds("reseted.ClipBounds", g.ClipBounds, -8, 0, 8, 8); + CheckBounds("reseted.Clip.GetBounds", g.Clip.GetBounds(g), -8, 0, 8, 8); + } + } + + [Fact] + public void Clip_ScaleTransform_NoBoundsChange() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + CheckBounds("graphics.ClipBounds", g.ClipBounds, 0, 0, 16, 16); + CheckBounds("graphics.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 16); + g.ScaleTransform(2, 0.5f); + CheckBounds("scaled.ClipBounds", g.ClipBounds, 0, 0, 8, 32); + CheckBounds("scaled.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 32); + g.Clip = new Region(new Rectangle(0, 0, 8, 8)); + // ClipBounds isn't affected by a previous scaling + CheckBounds("rectangle.ClipBounds", g.ClipBounds, 0, 0, 8, 8); + // Clip.GetBounds isn't affected by a previous scaling + CheckBounds("rectangle.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 8, 8); + + g.ResetTransform(); + CheckBounds("reseted.ClipBounds", g.ClipBounds, 0, 0, 16, 4); + CheckBounds("reseted.Clip.GetBounds", g.Clip.GetBounds(g), 0, 0, 16, 4); + } + } + + [Fact] + public void ScaleTransform_X0() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + Assert.Throws(() => g.ScaleTransform(0, 1)); + } + } + + [Fact] + public void ScaleTransform_Y0() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + Assert.Throws(() => g.ScaleTransform(1, 0)); + } + } + + [Fact] + public void TranslateTransform_Order() + { + using (var b = new BitmapAndGraphics(16, 16)) + { + var g = b.Graphics; + g.Transform = new Matrix(1, 2, 3, 4, 5, 6); + g.TranslateTransform(3, -3); + float[] elements = g.Transform.Elements; + Assert.Equal(1, elements[0]); + Assert.Equal(2, elements[1]); + Assert.Equal(3, elements[2]); + Assert.Equal(4, elements[3]); + Assert.Equal(-1, elements[4]); + Assert.Equal(0, elements[5]); + + g.Transform = new Matrix(1, 2, 3, 4, 5, 6); + g.TranslateTransform(3, -3, MatrixOrder.Prepend); + elements = g.Transform.Elements; + Assert.Equal(1, elements[0]); + Assert.Equal(2, elements[1]); + Assert.Equal(3, elements[2]); + Assert.Equal(4, elements[3]); + Assert.Equal(-1, elements[4]); + Assert.Equal(0, elements[5]); + + g.Transform = new Matrix(1, 2, 3, 4, 5, 6); + g.TranslateTransform(3, -3, MatrixOrder.Append); + elements = g.Transform.Elements; + Assert.Equal(1, elements[0]); + Assert.Equal(2, elements[1]); + Assert.Equal(3, elements[2]); + Assert.Equal(4, elements[3]); + Assert.Equal(8, elements[4]); + Assert.Equal(3, elements[5]); + } + } + + static PointF[] SmallCurveF = new PointF[3] { new PointF(0, 0), new PointF(15, 5), new PointF(5, 15) }; + + static Point[] TooSmallCurve = new Point[2] { new Point(0, 0), new Point(15, 5) }; + static PointF[] LargeCurveF = new PointF[4] { new PointF(0, 0), new PointF(15, 5), new PointF(5, 15), new PointF(0, 20) }; + + [Fact] + public void DrawCurve_NotEnoughPoints() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + CheckForEmptyBitmap(bitmap); + g.DrawCurve(Pens.Black, TooSmallCurve, 0.5f); + CheckForNonEmptyBitmap(bitmap); + // so a "curve" can be drawn with less than 3 points! + // actually I used to call that a line... (and it's not related to tension) + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawCurve_SinglePoint() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.DrawCurve(Pens.Black, new Point[1] { new Point(10, 10) }, 0.5f)); + // a single point isn't enough + } + } + + [Fact] + public void DrawCurve3_NotEnoughPoints() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.DrawCurve(Pens.Black, TooSmallCurve, 0, 2, 0.5f)); + // aha, this is API dependent + } + } + + [Fact] + public void DrawCurve_NegativeTension() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + // documented as bigger (or equals) to 0 + g.DrawCurve(Pens.Black, SmallCurveF, -0.9f); + CheckForNonEmptyBitmap(bitmap); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawCurve_PositiveTension() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.DrawCurve(Pens.Black, SmallCurveF, 0.9f); + // this is not the same as -1 + CheckForNonEmptyBitmap(bitmap); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawCurve_ZeroSegments() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.DrawCurve(Pens.Black, SmallCurveF, 0, 0)); + } + } + + [Fact] + public void DrawCurve_NegativeSegments() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.DrawCurve(Pens.Black, SmallCurveF, 0, -1)); + } + } + + [Fact] + public void DrawCurve_OffsetTooLarge() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + // starting offset 1 doesn't give 3 points to make a curve + Assert.Throws(() => g.DrawCurve(Pens.Black, SmallCurveF, 1, 2)); + // and in this case 2 points aren't enough to draw something + } + } + + [Fact] + public void DrawCurve_Offset_0() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.DrawCurve(Pens.Black, LargeCurveF, 0, 2, 0.5f); + CheckForNonEmptyBitmap(bitmap); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawCurve_Offset_1() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.DrawCurve(Pens.Black, LargeCurveF, 1, 2, 0.5f); + CheckForNonEmptyBitmap(bitmap); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawCurve_Offset_2() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + // it works even with two points because we know the previous ones + g.DrawCurve(Pens.Black, LargeCurveF, 2, 1, 0.5f); + CheckForNonEmptyBitmap(bitmap); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawRectangle_Negative() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (Pen pen = new Pen(Color.Red)) + { + g.DrawRectangle(pen, 5, 5, -10, -10); + g.DrawRectangle(pen, 0.0f, 0.0f, 5.0f, -10.0f); + g.DrawRectangle(pen, new Rectangle(15, 0, -10, 5)); + CheckForEmptyBitmap(bitmap); + pen.Dispose(); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void DrawRectangles_Negative() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (Pen pen = new Pen(Color.Red)) + { + Rectangle[] rects = new Rectangle[2] + { + new Rectangle (5, 5, -10, -10), + new Rectangle (0, 0, 5, -10) + }; + RectangleF[] rectf = new RectangleF[2] + { + new RectangleF (0.0f, 5.0f, -10.0f, -10.0f), + new RectangleF (15.0f, 0.0f, -10.0f, 5.0f) + }; + g.DrawRectangles(pen, rects); + g.DrawRectangles(pen, rectf); + CheckForEmptyBitmap(bitmap); + pen.Dispose(); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void FillRectangle_Negative() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (SolidBrush brush = new SolidBrush(Color.Red)) + { + g.FillRectangle(brush, 5, 5, -10, -10); + g.FillRectangle(brush, 0.0f, 0.0f, 5.0f, -10.0f); + g.FillRectangle(brush, new Rectangle(15, 0, -10, 5)); + CheckForEmptyBitmap(bitmap); + brush.Dispose(); + g.Dispose(); + bitmap.Dispose(); + } + } + + [Fact] + public void FillRectangles_Negative() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (SolidBrush brush = new SolidBrush(Color.Red)) + { + Rectangle[] rects = new Rectangle[2] + { + new Rectangle (5, 5, -10, -10), + new Rectangle (0, 0, 5, -10) + }; + + RectangleF[] rectf = new RectangleF[2] + { + new RectangleF (0.0f, 5.0f, -10.0f, -10.0f), + new RectangleF (15.0f, 0.0f, -10.0f, 5.0f) + }; + + g.FillRectangles(brush, rects); + g.FillRectangles(brush, rectf); + CheckForEmptyBitmap(bitmap); + brush.Dispose(); + g.Dispose(); + bitmap.Dispose(); + } + } + + private void CheckDefaultProperties(string message, Graphics g) + { + Assert.True(g.Clip.IsInfinite(g), message + ".Clip.IsInfinite"); + AssertEquals(message + ".CompositingMode", CompositingMode.SourceOver, g.CompositingMode); + AssertEquals(message + ".CompositingQuality", CompositingQuality.Default, g.CompositingQuality); + AssertEquals(message + ".InterpolationMode", InterpolationMode.Bilinear, g.InterpolationMode); + AssertEquals(message + ".PageScale", 1.0f, g.PageScale); + AssertEquals(message + ".PageUnit", GraphicsUnit.Display, g.PageUnit); + AssertEquals(message + ".PixelOffsetMode", PixelOffsetMode.Default, g.PixelOffsetMode); + AssertEquals(message + ".SmoothingMode", SmoothingMode.None, g.SmoothingMode); + AssertEquals(message + ".TextContrast", 4, g.TextContrast); + AssertEquals(message + ".TextRenderingHint", TextRenderingHint.SystemDefault, g.TextRenderingHint); + Assert.True(g.Transform.IsIdentity, message + ".Transform.IsIdentity"); + } + + private void CheckCustomProperties(string message, Graphics g) + { + Assert.False(g.Clip.IsInfinite(g), message + ".Clip.IsInfinite"); + AssertEquals(message + ".CompositingMode", CompositingMode.SourceCopy, g.CompositingMode); + AssertEquals(message + ".CompositingQuality", CompositingQuality.HighQuality, g.CompositingQuality); + AssertEquals(message + ".InterpolationMode", InterpolationMode.HighQualityBicubic, g.InterpolationMode); + AssertEquals(message + ".PageScale", 0.5f, g.PageScale); + AssertEquals(message + ".PageUnit", GraphicsUnit.Inch, g.PageUnit); + AssertEquals(message + ".PixelOffsetMode", PixelOffsetMode.Half, g.PixelOffsetMode); + AssertEquals(message + ".RenderingOrigin", new Point(-1, -1), g.RenderingOrigin); + AssertEquals(message + ".SmoothingMode", SmoothingMode.AntiAlias, g.SmoothingMode); + AssertEquals(message + ".TextContrast", 0, g.TextContrast); + AssertEquals(message + ".TextRenderingHint", TextRenderingHint.AntiAlias, g.TextRenderingHint); + Assert.False(g.Transform.IsIdentity, message + ".Transform.IsIdentity"); + } + + private void CheckMatrix(string message, Matrix m, float xx, float yx, float xy, float yy, float x0, float y0) + { + float[] elements = m.Elements; + AssertEquals(message + ".Matrix.xx", xx, elements[0], 2); + AssertEquals(message + ".Matrix.yx", yx, elements[1], 2); + AssertEquals(message + ".Matrix.xy", xy, elements[2], 2); + AssertEquals(message + ".Matrix.yy", yy, elements[3], 2); + AssertEquals(message + ".Matrix.x0", x0, elements[4], 2); + AssertEquals(message + ".Matrix.y0", y0, elements[5], 2); + } + + [Fact] + public void BeginContainer() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + CheckDefaultProperties("default", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + + g.Clip = new Region(new Rectangle(10, 10, 10, 10)); + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PageScale = 0.5f; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.RenderingOrigin = new Point(-1, -1); + g.RotateTransform(45); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextContrast = 0; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + CheckCustomProperties("modified", g); + CheckMatrix("modified.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + + GraphicsContainer gc = g.BeginContainer(); + // things gets reseted after calling BeginContainer + CheckDefaultProperties("BeginContainer", g); + // but not everything + Assert.Equal(new Point(-1, -1), g.RenderingOrigin); + + g.EndContainer(gc); + CheckCustomProperties("EndContainer", g); + } + } + + [Fact] + public void BeginContainer_Rect() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + CheckDefaultProperties("default", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + + g.Clip = new Region(new Rectangle(10, 10, 10, 10)); + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PageScale = 0.5f; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.RenderingOrigin = new Point(-1, -1); + g.RotateTransform(45); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextContrast = 0; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + CheckCustomProperties("modified", g); + CheckMatrix("modified.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + + GraphicsContainer gc = g.BeginContainer(new Rectangle(10, 20, 30, 40), new Rectangle(10, 20, 300, 400), GraphicsUnit.Millimeter); + // things gets reseted after calling BeginContainer + CheckDefaultProperties("BeginContainer", g); + // but not everything + Assert.Equal(new Point(-1, -1), g.RenderingOrigin); + + g.EndContainer(gc); + CheckCustomProperties("EndContainer", g); + CheckMatrix("EndContainer.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + } + } + + [Fact] + public void BeginContainer_RectF() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + CheckDefaultProperties("default", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + + g.Clip = new Region(new Rectangle(10, 10, 10, 10)); + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PageScale = 0.5f; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.RenderingOrigin = new Point(-1, -1); + g.RotateTransform(45); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextContrast = 0; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + CheckCustomProperties("modified", g); + CheckMatrix("modified.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + + GraphicsContainer gc = g.BeginContainer(new RectangleF(40, 30, 20, 10), new RectangleF(10, 20, 30, 40), GraphicsUnit.Inch); + // things gets reseted after calling BeginContainer + CheckDefaultProperties("BeginContainer", g); + // but not everything + Assert.Equal(new Point(-1, -1), g.RenderingOrigin); + + g.EndContainer(gc); + CheckCustomProperties("EndContainer", g); + } + } + + private void BeginContainer_GraphicsUnit(GraphicsUnit unit) + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.BeginContainer(new RectangleF(40, 30, 20, 10), new RectangleF(10, 20, 30, 40), unit); + } + } + + [Fact] + public void BeginContainer_GraphicsUnit_Display() + { + Assert.Throws(() => BeginContainer_GraphicsUnit(GraphicsUnit.Display)); + } + + [Fact] + public void BeginContainer_GraphicsUnit_Valid() + { + BeginContainer_GraphicsUnit(GraphicsUnit.Document); + BeginContainer_GraphicsUnit(GraphicsUnit.Inch); + BeginContainer_GraphicsUnit(GraphicsUnit.Millimeter); + BeginContainer_GraphicsUnit(GraphicsUnit.Pixel); + BeginContainer_GraphicsUnit(GraphicsUnit.Point); + } + + [Fact] + public void BeginContainer_GraphicsUnit_World() + { + Assert.Throws(() => BeginContainer_GraphicsUnit(GraphicsUnit.World)); + } + + [Fact] + public void BeginContainer_GraphicsUnit_Bad() + { + Assert.Throws(() => BeginContainer_GraphicsUnit((GraphicsUnit)int.MinValue)); + } + + [Fact] + public void EndContainer_Null() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.EndContainer(null)); + } + } + + [Fact] + public void Save() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + CheckDefaultProperties("default", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + + GraphicsState gs1 = g.Save(); + // nothing is changed after a save + CheckDefaultProperties("save1", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + + g.Clip = new Region(new Rectangle(10, 10, 10, 10)); + g.CompositingMode = CompositingMode.SourceCopy; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.PageScale = 0.5f; + g.PageUnit = GraphicsUnit.Inch; + g.PixelOffsetMode = PixelOffsetMode.Half; + g.RenderingOrigin = new Point(-1, -1); + g.RotateTransform(45); + g.SmoothingMode = SmoothingMode.AntiAlias; + g.TextContrast = 0; + g.TextRenderingHint = TextRenderingHint.AntiAlias; + CheckCustomProperties("modified", g); + CheckMatrix("modified.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + + GraphicsState gs2 = g.Save(); + CheckCustomProperties("save2", g); + + g.Restore(gs2); + CheckCustomProperties("restored1", g); + CheckMatrix("restored1.Transform", g.Transform, 0.707f, 0.707f, -0.707f, 0.707f, 0, 0); + + g.Restore(gs1); + CheckDefaultProperties("restored2", g); + Assert.Equal(new Point(0, 0), g.RenderingOrigin); + } + } + + [Fact] + public void Restore_Null() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.Restore(null)); + } + } + + [Fact] + public void FillRectangles_BrushNull_Rectangle() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(null, new Rectangle[1])); + } + } + + [Fact] + public void FillRectangles_Rectangle_Null() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(Brushes.Red, (Rectangle[])null)); + } + } + + [Fact] + public void FillRectanglesZeroRectangle() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(Brushes.Red, new Rectangle[0])); + } + } + + [Fact] + public void FillRectangles_BrushNull_RectangleF() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(null, new RectangleF[1])); + } + } + + [Fact] + public void FillRectangles_RectangleF_Null() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(Brushes.Red, (RectangleF[])null)); + } + } + + [Fact] + public void FillRectanglesZeroRectangleF() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.FillRectangles(Brushes.Red, new RectangleF[0])); + } + } + + [Fact] + public void FillRectangles_NormalBehavior() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + { + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.Clear(Color.Fuchsia); + Rectangle rect = new Rectangle(5, 5, 10, 10); + g.Clip = new Region(rect); + g.FillRectangle(Brushes.Red, rect); + } + Assert.Equal(Color.Red.ToArgb(), bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(Color.Red.ToArgb(), bitmap.GetPixel(14, 5).ToArgb()); + Assert.Equal(Color.Red.ToArgb(), bitmap.GetPixel(5, 14).ToArgb()); + Assert.Equal(Color.Red.ToArgb(), bitmap.GetPixel(14, 14).ToArgb()); + + Assert.Equal(Color.Fuchsia.ToArgb(), bitmap.GetPixel(15, 5).ToArgb()); + Assert.Equal(Color.Fuchsia.ToArgb(), bitmap.GetPixel(5, 15).ToArgb()); + Assert.Equal(Color.Fuchsia.ToArgb(), bitmap.GetPixel(15, 15).ToArgb()); + } + } + + private Bitmap FillDrawRectangle(float width) + { + Bitmap bitmap = new Bitmap(20, 20); + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.Clear(Color.Red); + Rectangle rect = new Rectangle(5, 5, 10, 10); + g.FillRectangle(Brushes.Green, rect); + if (width >= 0) + { + using (Pen pen = new Pen(Color.Blue, width)) + { + g.DrawRectangle(pen, rect); + } + } + else + { + g.DrawRectangle(Pens.Blue, rect); + } + } + return bitmap; + } + + [Fact] + public void FillDrawRectangle_Width_Default() + { + // default pen size + using (Bitmap bitmap = FillDrawRectangle(float.MinValue)) + { + // NW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 6).ToArgb()); + // N + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 6).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 6).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 14).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 14).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 14).ToArgb()); + // W + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 9).ToArgb()); + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void FillDrawRectangle_Width_2() + { + // even pen size + using (Bitmap bitmap = FillDrawRectangle(2.0f)) + { + // NW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 6).ToArgb()); + // N + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 6).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 6).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 13).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 13).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 13).ToArgb()); + // W + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 9).ToArgb()); + } + } + + [Fact] + public void FillDrawRectangle_Width_3() + { + // odd pen size + using (Bitmap bitmap = FillDrawRectangle(3.0f)) + { + // NW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(6, 6).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(7, 7).ToArgb()); + // N + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 5).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 6).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 7).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 5).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 6).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 7).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(13, 13).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 13).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(6, 14).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(7, 13).ToArgb()); + // W + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(6, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(7, 9).ToArgb()); + } + } + + // reverse, draw the fill over + private Bitmap DrawFillRectangle(float width) + { + Bitmap bitmap = new Bitmap(20, 20); + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.Clear(Color.Red); + Rectangle rect = new Rectangle(5, 5, 10, 10); + if (width >= 0) + { + using (Pen pen = new Pen(Color.Blue, width)) + { + g.DrawRectangle(pen, rect); + } + } + else + { + g.DrawRectangle(Pens.Blue, rect); + } + g.FillRectangle(Brushes.Green, rect); + } + return bitmap; + } + + [Fact] + public void DrawFillRectangle_Width_Default() + { + // default pen size + using (Bitmap bitmap = DrawFillRectangle(float.MinValue)) + { + // NW - no blue border + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 6).ToArgb()); + // N - no blue border + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 6).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 5).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 6).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 14).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 14).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 14).ToArgb()); + // W - no blue border + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 9).ToArgb()); + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void DrawFillRectangle_Width_2() + { + // even pen size + using (Bitmap bitmap = DrawFillRectangle(2.0f)) + { + // looks like a one pixel border - but enlarged + // NW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 5).ToArgb()); + // N + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 5).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 5).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 14).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 14).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 14).ToArgb()); + // W + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 9).ToArgb()); + } + } + + [Fact] + public void DrawFillRectangle_Width_3() + { + // odd pen size + using (Bitmap bitmap = DrawFillRectangle(3.0f)) + { + // NW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 5).ToArgb()); + // N + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 5).ToArgb()); + // NE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 3).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 4).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 5).ToArgb()); + // E + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 9).ToArgb()); + // SE + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(17, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(16, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(14, 14).ToArgb()); + // S + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(9, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(9, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(9, 14).ToArgb()); + // SW + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 17).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 16).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 15).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(6, 14).ToArgb()); + // W + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(3, 9).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(4, 9).ToArgb()); + Assert.Equal(0xFF008000, (uint)bitmap.GetPixel(5, 9).ToArgb()); + } + } + + private Bitmap DrawLines(float width) + { + Bitmap bitmap = new Bitmap(20, 20); + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.Clear(Color.Red); + Point[] pts = new Point[3] { new Point(5, 5), new Point(15, 5), new Point(15, 15) }; + if (width >= 0) + { + using (Pen pen = new Pen(Color.Blue, width)) + { + g.DrawLines(pen, pts); + } + } + else + { + g.DrawLines(Pens.Blue, pts); + } + } + return bitmap; + } + + [Fact] + public void DrawLines_Width_Default() + { + // default pen size + using (Bitmap bitmap = DrawLines(float.MinValue)) + { + // start + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 4).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 5).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(4, 6).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(5, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(5, 5).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(5, 6).ToArgb()); + // middle + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(14, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(14, 5).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(14, 6).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(15, 4).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 5).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 6).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 4).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 5).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 6).ToArgb()); + //end + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(14, 15).ToArgb()); + Assert.Equal(0xFF0000FF, (uint)bitmap.GetPixel(15, 15).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 15).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(14, 16).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(15, 16).ToArgb()); + Assert.Equal(0xFFFF0000, (uint)bitmap.GetPixel(16, 16).ToArgb()); + } + } + + [Fact] + public void MeasureString_StringFont() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + { + using (Graphics g = Graphics.FromImage(bitmap)) + { + SizeF size = g.MeasureString(null, font); + Assert.True(size.IsEmpty); + size = g.MeasureString(string.Empty, font); + Assert.True(size.IsEmpty); + // null font + size = g.MeasureString(null, null); + Assert.True(size.IsEmpty); + size = g.MeasureString(string.Empty, null); + Assert.True(size.IsEmpty); + } + } + } + + [Fact] + public void MeasureString_StringFont_Null() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.MeasureString("a", null)); + } + } + + [Fact] + public void MeasureString_StringFontSizeF() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + SizeF size = g.MeasureString("a", font, SizeF.Empty); + Assert.False(size.IsEmpty); + + size = g.MeasureString(string.Empty, font, SizeF.Empty); + Assert.True(size.IsEmpty); + } + } + + private void MeasureString_StringFontInt(string s) + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + SizeF size0 = g.MeasureString(s, font, 0); + SizeF sizeN = g.MeasureString(s, font, int.MinValue); + SizeF sizeP = g.MeasureString(s, font, int.MaxValue); + Assert.Equal(size0, sizeN); + Assert.Equal(size0, sizeP); + } + } + + [Fact] + public void MeasureString_StringFontInt_ShortString() + { + MeasureString_StringFontInt("a"); + } + + [Fact] + public void MeasureString_StringFontInt_LongString() + { + MeasureString_StringFontInt("A very long string..."); + } + + [Fact] + public void MeasureString_StringFormat_Alignment() + { + string text = "Hello Mono::"; + + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.Alignment = StringAlignment.Near; + SizeF near = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.Alignment = StringAlignment.Center; + SizeF center = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.Alignment = StringAlignment.Far; + SizeF far = g.MeasureString(text, font, int.MaxValue, string_format); + + Assert.Equal((double)near.Width, center.Width, 1); + Assert.Equal((double)near.Height, center.Height, 1); + + Assert.Equal((double)center.Width, far.Width, 1); + Assert.Equal((double)center.Height, far.Height, 1); + } + } + + [Fact] + public void MeasureString_StringFormat_Alignment_DirectionVertical() + { + string text = "Hello Mono::"; + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.FormatFlags = StringFormatFlags.DirectionVertical; + + string_format.Alignment = StringAlignment.Near; + SizeF near = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.Alignment = StringAlignment.Center; + SizeF center = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.Alignment = StringAlignment.Far; + SizeF far = g.MeasureString(text, font, int.MaxValue, string_format); + + Assert.Equal((double)near.Width, center.Width, 0); + Assert.Equal((double)near.Height, center.Height, 0); + + Assert.Equal((double)center.Width, far.Width, 0); + Assert.Equal((double)center.Height, far.Height, 0); + } + } + + [Fact] + public void MeasureString_StringFormat_LineAlignment() + { + string text = "Hello Mono::"; + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.LineAlignment = StringAlignment.Near; + SizeF near = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.LineAlignment = StringAlignment.Center; + SizeF center = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.LineAlignment = StringAlignment.Far; + SizeF far = g.MeasureString(text, font, int.MaxValue, string_format); + + Assert.Equal((double)near.Width, center.Width, 1); + Assert.Equal((double)near.Height, center.Height, 1); + + Assert.Equal((double)center.Width, far.Width, 1); + Assert.Equal((double)center.Height, far.Height, 1); + } + } + + [Fact] + public void MeasureString_StringFormat_LineAlignment_DirectionVertical() + { + string text = "Hello Mono::"; + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.FormatFlags = StringFormatFlags.DirectionVertical; + + string_format.LineAlignment = StringAlignment.Near; + SizeF near = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.LineAlignment = StringAlignment.Center; + SizeF center = g.MeasureString(text, font, int.MaxValue, string_format); + + string_format.LineAlignment = StringAlignment.Far; + SizeF far = g.MeasureString(text, font, int.MaxValue, string_format); + + Assert.Equal((double)near.Width, center.Width, 1); + Assert.Equal((double)near.Height, center.Height, 1); + + Assert.Equal((double)center.Width, far.Width, 1); + Assert.Equal((double)center.Height, far.Height, 1); + } + } + + [Fact] + public void MeasureString_CharactersFitted() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string s = "aaa aa aaaa a aaa"; + SizeF size = g.MeasureString(s, font); + + int chars, lines; + SizeF size2 = g.MeasureString(s, font, new SizeF(80, size.Height), null, out chars, out lines); + + // in pixels + Assert.True(size2.Width < size.Width); + Assert.Equal((double)size2.Height, size.Height); + + Assert.Equal(1, lines); + // documentation seems to suggest chars is total length + Assert.True(chars < s.Length); + } + } + + [Fact] + public void MeasureString_Whitespace() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string s = string.Empty; + SizeF size = g.MeasureString(s, font); + Assert.Equal(0, size.Height); + Assert.Equal(0, size.Width); + + s += " "; + SizeF expected = g.MeasureString(s, font); + for (int i = 1; i < 10; i++) + { + s += " "; + size = g.MeasureString(s, font); + Assert.Equal((double)expected.Height, size.Height, 1); + Assert.Equal((double)expected.Width, size.Width, 1); + } + + s = "a"; + expected = g.MeasureString(s, font); + s = " " + s; + size = g.MeasureString(s, font); + float space_width = size.Width - expected.Width; + for (int i = 1; i < 10; i++) + { + size = g.MeasureString(s, font); + Assert.Equal((double)expected.Height, size.Height, 1); + Assert.Equal((double)expected.Width + i * space_width, size.Width, 1); + s = " " + s; + } + + s = "a"; + expected = g.MeasureString(s, font); + for (int i = 1; i < 10; i++) + { + s = s + " "; + size = g.MeasureString(s, font); + Assert.Equal((double)expected.Height, size.Height, 1); + Assert.Equal((double)expected.Width, size.Width, 1); + } + } + } + + [Fact] + public void MeasureCharacterRanges_NullOrEmptyText() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Region[] regions = g.MeasureCharacterRanges(null, font, new RectangleF(), null); + Assert.Equal(0, regions.Length); + regions = g.MeasureCharacterRanges(string.Empty, font, new RectangleF(), null); + Assert.Equal(0, regions.Length); + // null font is ok with null or empty string + regions = g.MeasureCharacterRanges(null, null, new RectangleF(), null); + Assert.Equal(0, regions.Length); + regions = g.MeasureCharacterRanges(string.Empty, null, new RectangleF(), null); + Assert.Equal(0, regions.Length); + } + } + + [Fact] + public void MeasureCharacterRanges_EmptyStringFormat() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + // string format without character ranges + Region[] regions = g.MeasureCharacterRanges("Mono", font, new RectangleF(), new StringFormat()); + Assert.Equal(0, regions.Length); + } + } + + [Fact] + public void MeasureCharacterRanges_FontNull() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.MeasureCharacterRanges("a", null, new RectangleF(), null)); + } + } + + [Fact] + public void MeasureCharacterRanges_TwoLines() + { + string text = "this\nis a test"; + CharacterRange[] ranges = new CharacterRange[2]; + ranges[0] = new CharacterRange(0, 5); + ranges[1] = new CharacterRange(5, 9); + + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.FormatFlags = StringFormatFlags.NoClip; + string_format.SetMeasurableCharacterRanges(ranges); + + SizeF size = g.MeasureString(text, font, new Point(0, 0), string_format); + RectangleF layout_rect = new RectangleF(0.0f, 0.0f, size.Width, size.Height); + Region[] regions = g.MeasureCharacterRanges(text, font, layout_rect, string_format); + + Assert.Equal(2, regions.Length); + Assert.Equal(regions[0].GetBounds(g).Height, regions[1].GetBounds(g).Height); + } + } + + private void MeasureCharacterRanges(string text, int first, int length) + { + CharacterRange[] ranges = new CharacterRange[1]; + ranges[0] = new CharacterRange(first, length); + + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.FormatFlags = StringFormatFlags.NoClip; + string_format.SetMeasurableCharacterRanges(ranges); + + SizeF size = g.MeasureString(text, font, new Point(0, 0), string_format); + RectangleF layout_rect = new RectangleF(0.0f, 0.0f, size.Width, size.Height); + g.MeasureCharacterRanges(text, font, layout_rect, string_format); + } + } + + [Fact] + public void MeasureCharacterRanges_FirstTooFar() + { + string text = "this\nis a test"; + Assert.Throws(() => MeasureCharacterRanges(text, text.Length, 1)); + } + + [Fact] + public void MeasureCharacterRanges_LengthTooLong() + { + string text = "this\nis a test"; + Assert.Throws(() => MeasureCharacterRanges(text, 0, text.Length + 1)); + } + + [Fact] + public void MeasureCharacterRanges_Prefix() + { + string text = "Hello &Mono::"; + CharacterRange[] ranges = new CharacterRange[1]; + ranges[0] = new CharacterRange(5, 4); + + using (StringFormat string_format = new StringFormat()) + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + string_format.SetMeasurableCharacterRanges(ranges); + + SizeF size = g.MeasureString(text, font, new Point(0, 0), string_format); + RectangleF layout_rect = new RectangleF(0.0f, 0.0f, size.Width, size.Height); + + // here & is part of the measure and visible + string_format.HotkeyPrefix = HotkeyPrefix.None; + Region[] regions = g.MeasureCharacterRanges(text, font, layout_rect, string_format); + RectangleF bounds_none = regions[0].GetBounds(g); + + // here & is part of the measure (range) but visible as an underline + string_format.HotkeyPrefix = HotkeyPrefix.Show; + regions = g.MeasureCharacterRanges(text, font, layout_rect, string_format); + RectangleF bounds_show = regions[0].GetBounds(g); + Assert.True(bounds_show.Width < bounds_none.Width); + + // here & is part of the measure (range) but invisible + string_format.HotkeyPrefix = HotkeyPrefix.Hide; + regions = g.MeasureCharacterRanges(text, font, layout_rect, string_format); + RectangleF bounds_hide = regions[0].GetBounds(g); + Assert.Equal((double)bounds_hide.Width, bounds_show.Width); + } + } + + [Fact] + public void MeasureCharacterRanges_NullStringFormat() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + { + Assert.Throws(() => g.MeasureCharacterRanges("Mono", font, new RectangleF(), null)); + } + } + + static CharacterRange[] ranges = new CharacterRange[] { + new CharacterRange (0, 1), + new CharacterRange (1, 1), + new CharacterRange (2, 1) + }; + + Region[] Measure_Helper(Graphics gfx, RectangleF rect) + { + using (StringFormat format = StringFormat.GenericTypographic) + { + format.SetMeasurableCharacterRanges(ranges); + + using (Font font = new Font(FontFamily.GenericSerif, 11.0f)) + { + return gfx.MeasureCharacterRanges("abc", font, rect, format); + } + } + } + + [Fact] + public void Measure() + { + using (Graphics gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + Region[] zero = Measure_Helper(gfx, new RectangleF(0, 0, 0, 0)); + Assert.Equal(3, zero.Length); + + Region[] small = Measure_Helper(gfx, new RectangleF(0, 0, 100, 100)); + Assert.Equal(3, small.Length); + for (int i = 0; i < 3; i++) + { + RectangleF zb = zero[i].GetBounds(gfx); + RectangleF sb = small[i].GetBounds(gfx); + Assert.Equal(sb.X, zb.X); + Assert.Equal(sb.Y, zb.Y); + Assert.Equal((double)sb.Width, zb.Width); + Assert.Equal((double)sb.Height, zb.Height); + } + + Region[] max = Measure_Helper(gfx, new RectangleF(0, 0, float.MaxValue, float.MaxValue)); + Assert.Equal(3, max.Length); + for (int i = 0; i < 3; i++) + { + RectangleF zb = zero[i].GetBounds(gfx); + RectangleF mb = max[i].GetBounds(gfx); + Assert.Equal(mb.X, zb.X); + Assert.Equal(mb.Y, zb.Y); + Assert.Equal((double)mb.Width, zb.Width); + Assert.Equal((double)mb.Height, zb.Height); + } + } + } + + [Fact] + public void MeasureLimits() + { + using (Graphics gfx = Graphics.FromImage(new Bitmap(1, 1))) + { + Region[] min = Measure_Helper(gfx, new RectangleF(0, 0, float.MinValue, float.MinValue)); + Assert.Equal(3, min.Length); + for (int i = 0; i < 3; i++) + { + RectangleF mb = min[i].GetBounds(gfx); + Assert.Equal(-4194304.0f, mb.X); + Assert.Equal(-4194304.0f, mb.Y); + Assert.Equal(8388608.0f, mb.Width); + Assert.Equal(8388608.0f, mb.Height); + } + + Region[] neg = Measure_Helper(gfx, new RectangleF(0, 0, -20, -20)); + Assert.Equal(3, neg.Length); + for (int i = 0; i < 3; i++) + { + RectangleF mb = neg[i].GetBounds(gfx); + Assert.Equal(-4194304.0f, mb.X); + Assert.Equal(-4194304.0f, mb.Y); + Assert.Equal(8388608.0f, mb.Width); + Assert.Equal(8388608.0f, mb.Height); + } + } + } + + [Fact] + public void DrawString_EndlessLoop() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (StringFormat fmt = new StringFormat()) + { + Rectangle rect = Rectangle.Empty; + rect.Location = new Point(10, 10); + rect.Size = new Size(1, 20); + fmt.Alignment = StringAlignment.Center; + fmt.LineAlignment = StringAlignment.Center; + fmt.FormatFlags = StringFormatFlags.NoWrap; + fmt.Trimming = StringTrimming.EllipsisWord; + g.DrawString("Test String", font, Brushes.Black, rect, fmt); + } + } + + [Fact] + public void DrawString_EndlessLoop_Wrapping() + { + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (StringFormat fmt = new StringFormat()) + { + Rectangle rect = Rectangle.Empty; + rect.Location = new Point(10, 10); + rect.Size = new Size(1, 20); + fmt.Alignment = StringAlignment.Center; + fmt.LineAlignment = StringAlignment.Center; + fmt.Trimming = StringTrimming.EllipsisWord; + g.DrawString("Test String", font, Brushes.Black, rect, fmt); + } + } + + [ConditionalFact(Helpers.RecentGdiplusIsAvailable)] + public void MeasureString_Wrapping_Dots() + { + string text = "this is really long text........................................... with a lot o periods."; + using (Bitmap bitmap = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bitmap)) + using (StringFormat format = new StringFormat()) + { + format.Alignment = StringAlignment.Center; + SizeF sz = g.MeasureString(text, font, 80, format); + Assert.True(sz.Width <= 80); + Assert.True(sz.Height > font.Height * 2); + } + } + + [Fact] + public void GetReleaseHdcInternal() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + IntPtr hdc1 = g.GetHdc(); + g.ReleaseHdcInternal(hdc1); + IntPtr hdc2 = g.GetHdc(); + g.ReleaseHdcInternal(hdc2); + Assert.Equal(hdc1, hdc2); + } + } + + [Fact] + public void ReleaseHdcInternal_IntPtrZero() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + Assert.Throws(() => g.ReleaseHdcInternal(IntPtr.Zero)); + } + } + + [Fact] + public void ReleaseHdcInternal_TwoTimes() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + IntPtr hdc = g.GetHdc(); + g.ReleaseHdcInternal(hdc); + Assert.Throws(() => g.ReleaseHdcInternal(hdc)); + } + } + [Fact] + public void TestReleaseHdc() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + IntPtr hdc1 = g.GetHdc(); + g.ReleaseHdc(); + IntPtr hdc2 = g.GetHdc(); + g.ReleaseHdc(); + Assert.Equal(hdc1, hdc2); + } + } + + [Fact] + public void TestReleaseHdcException() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + Assert.Throws(() => g.ReleaseHdc()); + } + } + + [Fact] + public void TestReleaseHdcException2() + { + using (Bitmap b = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(b)) + { + g.GetHdc(); + g.ReleaseHdc(); + Assert.Throws(() => g.ReleaseHdc()); + } + } + [ConditionalFact] + public void VisibleClipBound() + { + if (PlatformDetection.IsArmOrArm64Process) + { + // [ActiveIssue("https://github.com/dotnet/runtime/issues/28859")] + throw new SkipTestException("Precision on float numbers"); + } + + // see #78958 + using (Bitmap bmp = new Bitmap(100, 100)) + using (Graphics g = Graphics.FromImage(bmp)) + { + RectangleF noclip = g.VisibleClipBounds; + Assert.Equal(0, noclip.X); + Assert.Equal(0, noclip.Y); + Assert.Equal(100, noclip.Width); + Assert.Equal(100, noclip.Height); + + // note: libgdiplus regions are precise to multiple of multiple of 8 + g.Clip = new Region(new RectangleF(0, 0, 32, 32)); + RectangleF clip = g.VisibleClipBounds; + Assert.Equal(0, clip.X); + Assert.Equal(0, clip.Y); + Assert.Equal(32.0, clip.Width, 4); + Assert.Equal(32.0, clip.Height, 4); + + g.RotateTransform(90); + RectangleF rotclip = g.VisibleClipBounds; + Assert.Equal(0, rotclip.X); + Assert.Equal(-32.0, rotclip.Y, 4); + Assert.Equal(32.0, rotclip.Width, 4); + Assert.Equal(32.0, rotclip.Height, 4); + } + } + + [ConditionalFact] + public void VisibleClipBound_BigClip() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (Bitmap bmp = new Bitmap(100, 100)) + using (Graphics g = Graphics.FromImage(bmp)) + { + RectangleF noclip = g.VisibleClipBounds; + Assert.Equal(0, noclip.X); + Assert.Equal(0, noclip.Y); + Assert.Equal(100, noclip.Width); + Assert.Equal(100, noclip.Height); + + // clip is larger than bitmap + g.Clip = new Region(new RectangleF(0, 0, 200, 200)); + RectangleF clipbound = g.ClipBounds; + Assert.Equal(0, clipbound.X); + Assert.Equal(0, clipbound.Y); + Assert.Equal(200, clipbound.Width); + Assert.Equal(200, clipbound.Height); + + RectangleF clip = g.VisibleClipBounds; + Assert.Equal(0, clip.X); + Assert.Equal(0, clip.Y); + Assert.Equal(100, clip.Width); + Assert.Equal(100, clip.Height); + + g.RotateTransform(90); + RectangleF rotclipbound = g.ClipBounds; + Assert.Equal(0, rotclipbound.X); + Assert.Equal(-200.0, rotclipbound.Y, 4); + Assert.Equal(200.0, rotclipbound.Width, 4); + Assert.Equal(200.0, rotclipbound.Height, 4); + + RectangleF rotclip = g.VisibleClipBounds; + Assert.Equal(0, rotclip.X); + Assert.Equal(-100.0, rotclip.Y, 4); + Assert.Equal(100.0, rotclip.Width, 4); + Assert.Equal(100.0, rotclip.Height, 4); + } + } + + [ConditionalFact] + public void Rotate() + { + if (PlatformDetection.IsArmOrArm64Process) + { + //ActiveIssue: 35744 + throw new SkipTestException("Precision on float numbers"); + } + + using (Bitmap bmp = new Bitmap(100, 50)) + using (Graphics g = Graphics.FromImage(bmp)) + { + RectangleF vcb = g.VisibleClipBounds; + Assert.Equal(0, vcb.X); + Assert.Equal(0, vcb.Y); + Assert.Equal(100.0, vcb.Width, 4); + Assert.Equal(50.0, vcb.Height, 4); + + g.RotateTransform(90); + RectangleF rvcb = g.VisibleClipBounds; + Assert.Equal(0, rvcb.X); + Assert.Equal(-100.0, rvcb.Y, 4); + Assert.Equal(50.0, rvcb.Width, 4); + Assert.Equal(100.0, rvcb.Height, 4); + } + } + + [Fact] + public void Scale() + { + using (Bitmap bmp = new Bitmap(100, 50)) + using (Graphics g = Graphics.FromImage(bmp)) + { + RectangleF vcb = g.VisibleClipBounds; + Assert.Equal(0, vcb.X); + Assert.Equal(0, vcb.Y); + Assert.Equal(100, vcb.Width); + Assert.Equal(50, vcb.Height); + + g.ScaleTransform(2, 0.5f); + RectangleF svcb = g.VisibleClipBounds; + Assert.Equal(0, svcb.X); + Assert.Equal(0, svcb.Y); + Assert.Equal(50, svcb.Width); + Assert.Equal(100, svcb.Height); + } + } + + [Fact] + public void Translate() + { + using (Bitmap bmp = new Bitmap(100, 50)) + using (Graphics g = Graphics.FromImage(bmp)) + { + RectangleF vcb = g.VisibleClipBounds; + Assert.Equal(0, vcb.X); + Assert.Equal(0, vcb.Y); + Assert.Equal(100, vcb.Width); + Assert.Equal(50, vcb.Height); + + g.TranslateTransform(-25, 25); + RectangleF tvcb = g.VisibleClipBounds; + Assert.Equal(25, tvcb.X); + Assert.Equal(-25, tvcb.Y); + Assert.Equal(100, tvcb.Width); + Assert.Equal(50, tvcb.Height); + } + } + + [Fact] + public void DrawIcon_NullRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawIcon(null, new Rectangle(0, 0, 32, 32))); + } + } + + [Fact] + public void DrawIcon_IconRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawIcon(SystemIcons.Application, new Rectangle(0, 0, 40, 20)); + // Rectangle is empty when X, Y, Width and Height == 0 + // (yep X and Y too, RectangleF only checks for Width and Height) + g.DrawIcon(SystemIcons.Asterisk, new Rectangle(0, 0, 0, 0)); + // so this one is half-empty ;-) + g.DrawIcon(SystemIcons.Error, new Rectangle(20, 40, 0, 0)); + // negative width or height isn't empty (for Rectangle) + g.DrawIconUnstretched(SystemIcons.WinLogo, new Rectangle(10, 20, -1, 0)); + g.DrawIconUnstretched(SystemIcons.WinLogo, new Rectangle(20, 10, 0, -1)); + } + } + + [Fact] + public void DrawIcon_NullIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawIcon(null, 4, 2)); + } + } + + [Fact] + public void DrawIcon_IconIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawIcon(SystemIcons.Exclamation, 4, 2); + g.DrawIcon(SystemIcons.Hand, 0, 0); + } + } + + [Fact] + public void DrawIconUnstretched_NullRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawIconUnstretched(null, new Rectangle(0, 0, 40, 20))); + } + } + + [Fact] + public void DrawIconUnstretched_IconRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawIconUnstretched(SystemIcons.Information, new Rectangle(0, 0, 40, 20)); + // Rectangle is empty when X, Y, Width and Height == 0 + // (yep X and Y too, RectangleF only checks for Width and Height) + g.DrawIconUnstretched(SystemIcons.Question, new Rectangle(0, 0, 0, 0)); + // so this one is half-empty ;-) + g.DrawIconUnstretched(SystemIcons.Warning, new Rectangle(20, 40, 0, 0)); + // negative width or height isn't empty (for Rectangle) + g.DrawIconUnstretched(SystemIcons.WinLogo, new Rectangle(10, 20, -1, 0)); + g.DrawIconUnstretched(SystemIcons.WinLogo, new Rectangle(20, 10, 0, -1)); + } + } + + [Fact] + public void DrawImage_NullRectangleF() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new RectangleF(0, 0, 0, 0))); + } + } + + [Fact] + public void DrawImage_ImageRectangleF() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, new RectangleF(0, 0, 0, 0)); + g.DrawImage(bmp, new RectangleF(20, 40, 0, 0)); + g.DrawImage(bmp, new RectangleF(10, 20, -1, 0)); + g.DrawImage(bmp, new RectangleF(20, 10, 0, -1)); + } + } + + [Fact] + public void DrawImage_NullPointF() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new PointF(0, 0))); + } + } + + [Fact] + public void DrawImage_ImagePointF() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, new PointF(0, 0)); + } + } + + [Fact] + public void DrawImage_NullPointFArray() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new PointF[0])); + } + } + + [Fact] + public void DrawImage_ImagePointFArrayNull() + { + using (Bitmap bmp = new Bitmap(40, 40)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, (PointF[])null)); + } + } + } + + [Fact] + public void DrawImage_ImagePointFArrayEmpty() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, new PointF[0])); + } + } + + [Fact] + public void DrawImage_ImagePointFArray() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, new PointF[] { + new PointF (0, 0), new PointF (1, 1), new PointF (2, 2) }); + } + } + + [Fact] + public void DrawImage_NullRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new Rectangle(0, 0, 0, 0))); + } + } + + [Fact] + public void DrawImage_ImageRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + // Rectangle is empty when X, Y, Width and Height == 0 + // (yep X and Y too, RectangleF only checks for Width and Height) + g.DrawImage(bmp, new Rectangle(0, 0, 0, 0)); + // so this one is half-empty ;-) + g.DrawImage(bmp, new Rectangle(20, 40, 0, 0)); + // negative width or height isn't empty (for Rectangle) + g.DrawImage(bmp, new Rectangle(10, 20, -1, 0)); + g.DrawImage(bmp, new Rectangle(20, 10, 0, -1)); + } + } + + [Fact] + public void DrawImage_NullPoint() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new Point(0, 0))); + } + } + + [Fact] + public void DrawImage_ImagePoint() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, new Point(0, 0)); + } + } + + [Fact] + public void DrawImage_NullPointArray() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new Point[0])); + } + } + + [Fact] + public void DrawImage_ImagePointArrayNull() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, (Point[])null)); + } + } + + [Fact] + public void DrawImage_ImagePointArrayEmpty() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, new Point[0])); + } + } + + [Fact] + public void DrawImage_ImagePointArray() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, new Point[] { + new Point (0, 0), new Point (1, 1), new Point (2, 2) }); + } + } + + [Fact] + public void DrawImage_NullIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, int.MaxValue, int.MinValue)); + } + } + + [Fact] + public void DrawImage_ImageIntInt_Overflow() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, int.MaxValue, int.MinValue)); + } + } + + [Fact] + public void DrawImage_ImageIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, -40, -40); + } + } + + [Fact] + public void DrawImage_NullFloat() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, float.MaxValue, float.MinValue)); + } + } + + [Fact] + public void DrawImage_ImageFloatFloat_Overflow() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(bmp, float.MaxValue, float.MinValue)); + } + } + + [Fact] + public void DrawImage_ImageFloatFloat() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, -40.0f, -40.0f); + } + } + + [Fact] + public void DrawImage_NullRectangleRectangleGraphicsUnit() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, new Rectangle(), new Rectangle(), GraphicsUnit.Display)); + } + } + + private void DrawImage_ImageRectangleRectangleGraphicsUnit(GraphicsUnit unit) + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Rectangle r = new Rectangle(0, 0, 40, 40); + g.DrawImage(bmp, r, r, unit); + } + } + + [Fact] + public void DrawImage_ImageRectangleRectangleGraphicsUnit_Display() + { + Assert.Throws(() => DrawImage_ImageRectangleRectangleGraphicsUnit(GraphicsUnit.Display)); + } + + [Fact] + public void DrawImage_ImageRectangleRectangleGraphicsUnit_Pixel() + { + // this unit works + DrawImage_ImageRectangleRectangleGraphicsUnit(GraphicsUnit.Pixel); + } + + [Fact] + public void DrawImage_ImageRectangleRectangleGraphicsUnit_World() + { + Assert.Throws(() => DrawImage_ImageRectangleRectangleGraphicsUnit(GraphicsUnit.World)); + } + + [Fact] + public void DrawImage_NullPointRectangleGraphicsUnit() + { + Rectangle r = new Rectangle(1, 2, 3, 4); + Point[] pts = new Point[3] { new Point(1, 1), new Point(2, 2), new Point(3, 3) }; + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, pts, r, GraphicsUnit.Pixel)); + } + } + + private void DrawImage_ImagePointRectangleGraphicsUnit(Point[] pts) + { + Rectangle r = new Rectangle(1, 2, 3, 4); + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, pts, r, GraphicsUnit.Pixel); + } + } + + [Fact] + public void DrawImage_ImageNullRectangleGraphicsUnit() + { + Assert.Throws(() => DrawImage_ImagePointRectangleGraphicsUnit(null)); + } + + [Fact] + public void DrawImage_ImagePoint0RectangleGraphicsUnit() + { + Assert.Throws(() => DrawImage_ImagePointRectangleGraphicsUnit(new Point[0])); + } + + [Fact] + public void DrawImage_ImagePoint1RectangleGraphicsUnit() + { + Point p = new Point(1, 1); + Assert.Throws(() => DrawImage_ImagePointRectangleGraphicsUnit(new Point[1] { p })); + } + + [Fact] + public void DrawImage_ImagePoint2RectangleGraphicsUnit() + { + Point p = new Point(1, 1); + Assert.Throws(() => DrawImage_ImagePointRectangleGraphicsUnit(new Point[2] { p, p })); + } + + [Fact] + public void DrawImage_ImagePoint3RectangleGraphicsUnit() + { + Point p = new Point(1, 1); + DrawImage_ImagePointRectangleGraphicsUnit(new Point[3] { p, p, p }); + } + + [Fact] + public void DrawImage_ImagePoint4RectangleGraphicsUnit() + { + Point p = new Point(1, 1); + Assert.Throws(() => DrawImage_ImagePointRectangleGraphicsUnit(new Point[4] { p, p, p, p })); + } + + [Fact] + public void DrawImage_NullPointFRectangleGraphicsUnit() + { + Rectangle r = new Rectangle(1, 2, 3, 4); + PointF[] pts = new PointF[3] { new PointF(1, 1), new PointF(2, 2), new PointF(3, 3) }; + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImage(null, pts, r, GraphicsUnit.Pixel)); + } + } + + private void DrawImage_ImagePointFRectangleGraphicsUnit(PointF[] pts) + { + Rectangle r = new Rectangle(1, 2, 3, 4); + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, pts, r, GraphicsUnit.Pixel); + } + } + + [Fact] + public void DrawImage_ImageNullFRectangleGraphicsUnit() + { + Assert.Throws(() => DrawImage_ImagePointFRectangleGraphicsUnit(null)); + } + + [Fact] + public void DrawImage_ImagePointF0RectangleGraphicsUnit() + { + Assert.Throws(() => DrawImage_ImagePointFRectangleGraphicsUnit(new PointF[0])); + } + + [Fact] + public void DrawImage_ImagePointF1RectangleGraphicsUnit() + { + PointF p = new PointF(1, 1); + Assert.Throws(() => DrawImage_ImagePointFRectangleGraphicsUnit(new PointF[1] { p })); + } + + [Fact] + public void DrawImage_ImagePointF2RectangleGraphicsUnit() + { + PointF p = new PointF(1, 1); + Assert.Throws(() => DrawImage_ImagePointFRectangleGraphicsUnit(new PointF[2] { p, p })); + } + + [Fact] + public void DrawImage_ImagePointF3RectangleGraphicsUnit() + { + PointF p = new PointF(1, 1); + DrawImage_ImagePointFRectangleGraphicsUnit(new PointF[3] { p, p, p }); + } + + [Fact] + public void DrawImage_ImagePointF4RectangleGraphicsUnit() + { + PointF p = new PointF(1, 1); + Assert.Throws(() => DrawImage_ImagePointFRectangleGraphicsUnit(new PointF[4] { p, p, p, p })); + } + + [Fact] + public void DrawImage_ImagePointRectangleGraphicsUnitNull() + { + Point p = new Point(1, 1); + Point[] pts = new Point[3] { p, p, p }; + Rectangle r = new Rectangle(1, 2, 3, 4); + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImage(bmp, pts, r, GraphicsUnit.Pixel, null); + } + } + + [Fact] + public void DrawImage_ImagePointRectangleGraphicsUnitAttributes() + { + Point p = new Point(1, 1); + Point[] pts = new Point[3] { p, p, p }; + Rectangle r = new Rectangle(1, 2, 3, 4); + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + using (ImageAttributes ia = new ImageAttributes()) + { + g.DrawImage(bmp, pts, r, GraphicsUnit.Pixel, ia); + } + } + + [Fact] + public void DrawImageUnscaled_NullPoint() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImageUnscaled(null, new Point(0, 0))); + } + } + + [Fact] + public void DrawImageUnscaled_ImagePoint() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImageUnscaled(bmp, new Point(0, 0)); + } + } + + [Fact] + public void DrawImageUnscaled_NullRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImageUnscaled(null, new Rectangle(0, 0, -1, -1))); + } + } + + [Fact] + public void DrawImageUnscaled_ImageRectangle() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImageUnscaled(bmp, new Rectangle(0, 0, -1, -1)); + } + } + + [Fact] + public void DrawImageUnscaled_NullIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImageUnscaled(null, 0, 0)); + } + } + + [Fact] + public void DrawImageUnscaled_ImageIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImageUnscaled(bmp, 0, 0); + } + } + + [Fact] + public void DrawImageUnscaled_NullIntIntIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImageUnscaled(null, 0, 0, -1, -1)); + } + } + + [Fact] + public void DrawImageUnscaled_ImageIntIntIntInt() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + g.DrawImageUnscaled(bmp, 0, 0, -1, -1); + } + } + + [Fact] + public void DrawImageUnscaledAndClipped_Null() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawImageUnscaledAndClipped(null, new Rectangle(0, 0, 0, 0))); + } + } + + [Fact] + public void DrawImageUnscaledAndClipped() + { + using (Bitmap bmp = new Bitmap(40, 40)) + using (Graphics g = Graphics.FromImage(bmp)) + { + // Rectangle is empty when X, Y, Width and Height == 0 + // (yep X and Y too, RectangleF only checks for Width and Height) + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(0, 0, 0, 0)); + // so this one is half-empty ;-) + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(20, 40, 0, 0)); + // negative width or height isn't empty (for Rectangle) + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(10, 20, -1, 0)); + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(20, 10, 0, -1)); + // smaller + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(0, 0, 10, 20)); + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(0, 0, 40, 10)); + g.DrawImageUnscaledAndClipped(bmp, new Rectangle(0, 0, 80, 20)); + } + } + + [Fact] + public void DrawPath_Pen_Null() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + using (GraphicsPath path = new GraphicsPath()) + { + Assert.Throws(() => g.DrawPath(null, path)); + } + } + + [Fact] + public void DrawPath_Path_Null() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.DrawPath(Pens.Black, null)); + } + } + + [Fact] + public void DrawPath_Arcs() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + using (GraphicsPath path = new GraphicsPath()) + { + int d = 5; + Rectangle baserect = new Rectangle(0, 0, 19, 19); + Rectangle arcrect = new Rectangle(baserect.Location, new Size(d, d)); + + path.AddArc(arcrect, 180, 90); + arcrect.X = baserect.Right - d; + path.AddArc(arcrect, 270, 90); + arcrect.Y = baserect.Bottom - d; + path.AddArc(arcrect, 0, 90); + arcrect.X = baserect.Left; + path.AddArc(arcrect, 90, 90); + path.CloseFigure(); + g.Clear(Color.White); + g.DrawPath(Pens.SteelBlue, path); + + Assert.Equal(-12156236, bmp.GetPixel(0, 9).ToArgb()); + Assert.Equal(-1, bmp.GetPixel(1, 9).ToArgb()); + } + } + + [Fact] + public void FillPath_Brush_Null() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + using (GraphicsPath path = new GraphicsPath()) + { + Assert.Throws(() => g.FillPath(null, path)); + } + } + + [Fact] + public void FillPath_Path_Null() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Throws(() => g.FillPath(Brushes.Black, null)); + } + } + + [Fact] + public void FillPath_Arcs() + { + using (Bitmap bmp = new Bitmap(20, 20)) + using (Graphics g = Graphics.FromImage(bmp)) + using (GraphicsPath path = new GraphicsPath()) + { + int d = 5; + Rectangle baserect = new Rectangle(0, 0, 19, 19); + Rectangle arcrect = new Rectangle(baserect.Location, new Size(d, d)); + + path.AddArc(arcrect, 180, 90); + arcrect.X = baserect.Right - d; + path.AddArc(arcrect, 270, 90); + arcrect.Y = baserect.Bottom - d; + path.AddArc(arcrect, 0, 90); + arcrect.X = baserect.Left; + path.AddArc(arcrect, 90, 90); + path.CloseFigure(); + g.Clear(Color.White); + g.FillPath(Brushes.SteelBlue, path); + + Assert.Equal(-12156236, bmp.GetPixel(0, 9).ToArgb()); + Assert.Equal(-12156236, bmp.GetPixel(1, 9).ToArgb()); + } + } + + [Fact] + public void TransformPoints() + { + using (Bitmap bmp = new Bitmap(10, 10)) + using (Graphics g = Graphics.FromImage(bmp)) + { + Point[] pts = new Point[5]; + PointF[] ptf = new PointF[5]; + for (int i = 0; i < 5; i++) + { + pts[i] = new Point(i, i); + ptf[i] = new PointF(i, i); + } + + g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, pts); + g.TransformPoints(CoordinateSpace.Page, CoordinateSpace.Device, ptf); + + for (int i = 0; i < 5; i++) + { + Assert.Equal(i, pts[i].X); + Assert.Equal(i, pts[i].Y); + Assert.Equal(i, ptf[i].X); + Assert.Equal(i, ptf[i].Y); + } + } + } + + [Fact] + public void Dpi() + { + float x, y; + using (Bitmap bmp = new Bitmap(10, 10)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + x = g.DpiX - 10; + y = g.DpiY + 10; + } + bmp.SetResolution(x, y); + using (Graphics g = Graphics.FromImage(bmp)) + { + Assert.Equal(x, g.DpiX); + Assert.Equal(y, g.DpiY); + } + } + } + + [Fact] + public void GetReleaseHdc() + { + using (Bitmap b = new Bitmap(100, 100)) + { + using (Graphics g = Graphics.FromImage(b)) + { + IntPtr hdc1 = g.GetHdc(); + g.ReleaseHdc(hdc1); + IntPtr hdc2 = g.GetHdc(); + g.ReleaseHdc(hdc2); + Assert.Equal(hdc1, hdc2); + } + } + } + } +} diff --git a/src/System.Drawing.Common/tests/mono/System.Imaging/MetafileTest.cs b/src/System.Drawing.Common/tests/mono/System.Imaging/MetafileTest.cs new file mode 100644 index 00000000000..d8085849f6c --- /dev/null +++ b/src/System.Drawing.Common/tests/mono/System.Imaging/MetafileTest.cs @@ -0,0 +1,466 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// +// Metafile class unit tests +// +// Authors: +// Sebastien Pouliot +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Runtime.InteropServices; +using Xunit; + +namespace MonoTests.System.Drawing.Imaging +{ + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class MetafileTest + { + public const string Bitmap = "non-inverted.bmp"; + public const string WmfPlaceable = "telescope_01.wmf"; + public const string Emf = "milkmateya01.emf"; + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_String() + { + string filename = Helpers.GetTestBitmapPath(WmfPlaceable); + using (Metafile mf = new Metafile(filename)) + using (Metafile clone = (Metafile)mf.Clone()) + { + } + } + + private static void Check_MetaHeader_WmfPlaceable(MetaHeader mh) + { + Assert.Equal(9, mh.HeaderSize); + Assert.Equal(98, mh.MaxRecord); + Assert.Equal(3, mh.NoObjects); + Assert.Equal(0, mh.NoParameters); + Assert.Equal(1737, mh.Size); + Assert.Equal(1, mh.Type); + Assert.Equal(0x300, mh.Version); + } + + private static void Check_MetafileHeader_WmfPlaceable(MetafileHeader header) + { + Assert.Equal(MetafileType.WmfPlaceable, header.Type); + Assert.Equal(0x300, header.Version); + // filesize - 22, which happens to be the size (22) of a PLACEABLEMETAHEADER struct + Assert.Equal(3474, header.MetafileSize); + + Assert.Equal(-30, header.Bounds.X); + Assert.Equal(-40, header.Bounds.Y); + Assert.Equal(3096, header.Bounds.Width); + Assert.Equal(4127, header.Bounds.Height); + Assert.Equal(606, header.DpiX); + Assert.Equal(606, header.DpiY); + Assert.Equal(0, header.EmfPlusHeaderSize); + Assert.Equal(0, header.LogicalDpiX); + Assert.Equal(0, header.LogicalDpiY); + + Assert.NotNull(header.WmfHeader); + Check_MetaHeader_WmfPlaceable(header.WmfHeader); + + Assert.False(header.IsDisplay()); + Assert.False(header.IsEmf()); + Assert.False(header.IsEmfOrEmfPlus()); + Assert.False(header.IsEmfPlus()); + Assert.False(header.IsEmfPlusDual()); + Assert.False(header.IsEmfPlusOnly()); + Assert.True(header.IsWmf()); + Assert.True(header.IsWmfPlaceable()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromFile_WmfPlaceable() + { + using (Metafile mf = new Metafile(Helpers.GetTestBitmapPath(WmfPlaceable))) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_WmfPlaceable(header1); + + MetaHeader mh1 = header1.WmfHeader; + Check_MetaHeader_WmfPlaceable(mh1); + + MetaHeader mh2 = mf.GetMetafileHeader().WmfHeader; + Assert.NotSame(mh1, mh2); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromFileStream_WmfPlaceable() + { + using (FileStream fs = File.OpenRead(Helpers.GetTestBitmapPath(WmfPlaceable))) + using (Metafile mf = new Metafile(fs)) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_WmfPlaceable(header1); + + MetaHeader mh1 = header1.WmfHeader; + Check_MetaHeader_WmfPlaceable(mh1); + + MetaHeader mh2 = mf.GetMetafileHeader().WmfHeader; + Assert.NotSame(mh1, mh2); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromMemoryStream_WmfPlaceable() + { + string filename = Helpers.GetTestBitmapPath(WmfPlaceable); + using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(filename))) + using (Metafile mf = new Metafile(ms)) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_WmfPlaceable(header1); + + MetaHeader mh1 = header1.WmfHeader; + Check_MetaHeader_WmfPlaceable(mh1); + + MetaHeader mh2 = mf.GetMetafileHeader().WmfHeader; + Assert.NotSame(mh1, mh2); + } + } + + private static void Check_MetafileHeader_Emf(MetafileHeader header) + { + Assert.Equal(MetafileType.Emf, header.Type); + Assert.Equal(65536, header.Version); + // exactly the filesize + Assert.Equal(20456, header.MetafileSize); + + Assert.Equal(0, header.Bounds.X); + Assert.Equal(0, header.Bounds.Y); + + Assert.Throws(() => header.WmfHeader); + + Assert.False(header.IsDisplay()); + Assert.True(header.IsEmf()); + Assert.True(header.IsEmfOrEmfPlus()); + Assert.False(header.IsEmfPlus()); + Assert.False(header.IsEmfPlusDual()); + Assert.False(header.IsEmfPlusOnly()); + Assert.False(header.IsWmf()); + Assert.False(header.IsWmfPlaceable()); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromFile_Emf() + { + using (Metafile mf = new Metafile(Helpers.GetTestBitmapPath(Emf))) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_Emf(header1); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromFileStream_Emf() + { + using (FileStream fs = File.OpenRead(Helpers.GetTestBitmapPath(Emf))) + using (Metafile mf = new Metafile(fs)) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_Emf(header1); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void GetMetafileHeader_FromMemoryStream_Emf() + { + string filename = Helpers.GetTestBitmapPath(Emf); + using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(filename))) + using (Metafile mf = new Metafile(ms)) + { + MetafileHeader header1 = mf.GetMetafileHeader(); + Check_MetafileHeader_Emf(header1); + } + } + } + + [ActiveIssue("https://github.com/dotnet/runtime/issues/34591", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] + public class MetafileFulltrustTest + { + private void CheckEmptyHeader(Metafile mf, EmfType type) + { + MetafileHeader mh = mf.GetMetafileHeader(); + Assert.Equal(0, mh.Bounds.X); + Assert.Equal(0, mh.Bounds.Y); + Assert.Equal(0, mh.Bounds.Width); + Assert.Equal(0, mh.Bounds.Height); + Assert.Equal(0, mh.MetafileSize); + switch (type) + { + case EmfType.EmfOnly: + Assert.Equal(MetafileType.Emf, mh.Type); + break; + case EmfType.EmfPlusDual: + Assert.Equal(MetafileType.EmfPlusDual, mh.Type); + break; + case EmfType.EmfPlusOnly: + Assert.Equal(MetafileType.EmfPlusOnly, mh.Type); + break; + default: + Assert.True(false, string.Format("Unknown EmfType '{0}'", type)); + break; + } + } + + private void Metafile_IntPtrEmfType(EmfType type) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + Metafile mf = new Metafile(hdc, type); + CheckEmptyHeader(mf, type); + } + finally + { + g.ReleaseHdc(hdc); + } + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_IntPtrRectangle_Empty() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + Metafile mf = new Metafile(hdc, new Rectangle()); + CheckEmptyHeader(mf, EmfType.EmfPlusDual); + } + finally + { + g.ReleaseHdc(hdc); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_IntPtrRectangleF_Empty() + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + Metafile mf = new Metafile(hdc, new RectangleF()); + CheckEmptyHeader(mf, EmfType.EmfPlusDual); + } + finally + { + g.ReleaseHdc(hdc); + } + } + } + + private void Metafile_StreamEmfType(Stream stream, EmfType type) + { + using (Bitmap bmp = new Bitmap(10, 10, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + Metafile mf = new Metafile(stream, hdc, type); + CheckEmptyHeader(mf, type); + } + finally + { + g.ReleaseHdc(hdc); + } + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_StreamIntPtrEmfType_Null() + { + Assert.Throws(() => Metafile_StreamEmfType(null, EmfType.EmfOnly)); + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_StreamIntPtrEmfType_EmfOnly() + { + using (MemoryStream ms = new MemoryStream()) + { + Metafile_StreamEmfType(ms, EmfType.EmfOnly); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Metafile_StreamIntPtrEmfType_Invalid() + { + using (MemoryStream ms = new MemoryStream()) + { + Assert.Throws(() => Metafile_StreamEmfType(ms, (EmfType)int.MinValue)); + } + } + + private void CreateFilename(EmfType type, bool single) + { + string name = string.Format("{0}-{1}.emf", type, single ? "Single" : "Multiple"); + string filename = Path.Combine(Path.GetTempPath(), name); + Metafile mf; + using (Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + mf = new Metafile(filename, hdc, type); + Assert.Equal(0, new FileInfo(filename).Length); + } + finally + { + g.ReleaseHdc(hdc); + } + } + long size = 0; + using (Graphics g = Graphics.FromImage(mf)) + { + g.FillRectangle(Brushes.BlueViolet, 10, 10, 80, 80); + size = new FileInfo(filename).Length; + Assert.Equal(0, size); + } + + if (!single) + { + using (Graphics g = Graphics.FromImage(mf)) + { + g.DrawRectangle(Pens.Azure, 10, 10, 80, 80); + } + } + mf.Dispose(); + Assert.Equal(size, new FileInfo(filename).Length); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void Measure() + { + Font test_font = new Font(FontFamily.GenericMonospace, 12); + + Metafile mf; + using (Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb)) + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + mf = new Metafile(hdc, EmfType.EmfPlusOnly); + } + finally + { + g.ReleaseHdc(hdc); + } + } + using (Graphics g = Graphics.FromImage(mf)) + { + string text = "this\nis a test"; + CharacterRange[] ranges = new CharacterRange[2]; + ranges[0] = new CharacterRange(0, 5); + ranges[1] = new CharacterRange(5, 9); + + SizeF size = g.MeasureString(text, test_font); + Assert.False(size.IsEmpty); + + StringFormat sf = new StringFormat(); + sf.FormatFlags = StringFormatFlags.NoClip; + sf.SetMeasurableCharacterRanges(ranges); + + RectangleF rect = new RectangleF(0, 0, size.Width, size.Height); + Region[] region = g.MeasureCharacterRanges(text, test_font, rect, sf); + Assert.Equal(2, region.Length); + mf.Dispose(); + } + } + + [ConditionalFact(Helpers.IsDrawingSupported)] + public void WorldTransforms() + { + Metafile mf; + using (Bitmap bmp = new Bitmap(100, 100, PixelFormat.Format32bppArgb)) + { + using (Graphics g = Graphics.FromImage(bmp)) + { + IntPtr hdc = g.GetHdc(); + try + { + mf = new Metafile(hdc, EmfType.EmfPlusOnly); + } + finally + { + g.ReleaseHdc(hdc); + } + } + using (Graphics g = Graphics.FromImage(mf)) + { + Assert.True(g.Transform.IsIdentity); + g.ScaleTransform(2f, 0.5f); + Assert.False(g.Transform.IsIdentity); + g.RotateTransform(90); + g.TranslateTransform(-2, 2); + Matrix m = g.Transform; + g.MultiplyTransform(m); + // check + float[] elements = g.Transform.Elements; + Assert.Equal(-1.0, elements[0], 5); + Assert.Equal(0.0, elements[1], 5); + Assert.Equal(0.0, elements[2], 5); + Assert.Equal(-1.0, elements[3], 5); + Assert.Equal(-2.0, elements[4], 5); + Assert.Equal(-3.0, elements[5], 5); + + g.Transform = m; + elements = g.Transform.Elements; + Assert.Equal(0.0, elements[0], 5); + Assert.Equal(0.5, elements[1], 5); + Assert.Equal(-2.0, elements[2], 5); + Assert.Equal(0.0, elements[3], 5); + Assert.Equal(-4.0, elements[4], 5); + Assert.Equal(-1.0, elements[5], 5); + + g.ResetTransform(); + Assert.True(g.Transform.IsIdentity); + } + mf.Dispose(); + } + } + } +} diff --git a/src/System.Drawing/src/System.Drawing.Facade.csproj b/src/System.Drawing/src/System.Drawing.Facade.csproj index ba0038e364b..9618333b47d 100644 --- a/src/System.Drawing/src/System.Drawing.Facade.csproj +++ b/src/System.Drawing/src/System.Drawing.Facade.csproj @@ -9,9 +9,9 @@ + - diff --git a/src/System.Windows.Forms.Primitives/src/System.Windows.Forms.Primitives.csproj b/src/System.Windows.Forms.Primitives/src/System.Windows.Forms.Primitives.csproj index 076c866112b..a9395d91100 100644 --- a/src/System.Windows.Forms.Primitives/src/System.Windows.Forms.Primitives.csproj +++ b/src/System.Windows.Forms.Primitives/src/System.Windows.Forms.Primitives.csproj @@ -29,13 +29,13 @@ + - diff --git a/src/System.Windows.Forms.Primitives/tests/TestUtilities/Extensions/AssertExtensions.cs b/src/System.Windows.Forms.Primitives/tests/TestUtilities/Extensions/AssertExtensions.cs index 1c4cf4cf2d3..18da6856f76 100644 --- a/src/System.Windows.Forms.Primitives/tests/TestUtilities/Extensions/AssertExtensions.cs +++ b/src/System.Windows.Forms.Primitives/tests/TestUtilities/Extensions/AssertExtensions.cs @@ -1,55 +1,1102 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. using System.Runtime.InteropServices; using System.Windows.Forms; using Xunit; +using Xunit.Sdk; using static Interop; namespace System { public static class AssertExtensions { - public static T Throws(string paramName, Func testCode) + private static bool IsNetFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework"); + + internal static void True(AccessibleObject accessibleObject, UiaCore.UIA propertyId) + { + Assert.True((bool)accessibleObject.GetPropertyValue(propertyId)); + } + + internal static void False(AccessibleObject accessibleObject, UiaCore.UIA propertyId) + { + Assert.False((bool)accessibleObject.GetPropertyValue(propertyId)); + } + + public static void Throws(Action action, string expectedMessage) + where T : Exception + { + Assert.Equal(expectedMessage, Assert.Throws(action).Message); + } + + public static void ThrowsContains(Action action, string expectedMessageContent) + where T : Exception + { + Assert.Contains(expectedMessageContent, Assert.Throws(action).Message); + } + + public static T Throws(string netCoreParamName, string netFxParamName, Action action) + where T : ArgumentException + { + T exception = Assert.Throws(action); + + if (netFxParamName is null && IsNetFramework) + { + // Param name varies between .NET Framework versions -- skip checking it + return exception; + } + + string expectedParamName = + IsNetFramework ? + netFxParamName : netCoreParamName; + + Assert.Equal(expectedParamName, exception.ParamName); + return exception; + } + + public static void Throws(string netCoreParamName, string netFxParamName, Func testCode) where T : ArgumentException { T exception = Assert.Throws(testCode); - if (!RuntimeInformation.FrameworkDescription.StartsWith(".NET Native")) + if (netFxParamName is null && IsNetFramework) { - Assert.Equal(paramName, exception.ParamName); + // Param name varies between .NET Framework versions -- skip checking it + return; } + string expectedParamName = + IsNetFramework ? + netFxParamName : netCoreParamName; + + Assert.Equal(expectedParamName, exception.ParamName); + } + + public static T Throws(string expectedParamName, Action action) + where T : ArgumentException + { + T exception = Assert.Throws(action); + + Assert.Equal(expectedParamName, exception.ParamName); + return exception; } - internal static void True(AccessibleObject accessibleObject, UiaCore.UIA propertyId) + public static T Throws(Action action) + where T : Exception { - Assert.True((bool)accessibleObject.GetPropertyValue(propertyId)); + T exception = Assert.Throws(action); + + return exception; } - internal static void False(AccessibleObject accessibleObject, UiaCore.UIA propertyId) + public static TException Throws(Func func) + where TException : Exception { - Assert.False((bool)accessibleObject.GetPropertyValue(propertyId)); + object result = null; + bool returned = false; + try + { + return + Assert.Throws(() => + { + result = func(); + returned = true; + }); + } + catch (Exception ex) when (returned) + { + string resultStr; + if (result is null) + { + resultStr = "(null)"; + } + else + { + resultStr = result.ToString(); + if (typeof(TResult) == typeof(string)) + { + resultStr = $"\"{resultStr}\""; + } + } + + throw new AggregateException($"Result: {resultStr}", ex); + } + } + + public static T Throws(string expectedParamName, Func testCode) + where T : ArgumentException + { + T exception = Assert.Throws(testCode); + + Assert.Equal(expectedParamName, exception.ParamName); + + return exception; + } + + public static async Task ThrowsAsync(string expectedParamName, Func testCode) + where T : ArgumentException + { + T exception = await Assert.ThrowsAsync(testCode); + + Assert.Equal(expectedParamName, exception.ParamName); + + return exception; + } + + public static void Throws(string expectedParamName, Action action) + where TNetCoreExceptionType : ArgumentException + where TNetFxExceptionType : Exception + { + if (IsNetFramework) + { + // Support cases where the .NET Core exception derives from ArgumentException + // but the .NET Framework exception is not. + if (typeof(ArgumentException).IsAssignableFrom(typeof(TNetFxExceptionType))) + { + Exception exception = Assert.Throws(typeof(TNetFxExceptionType), action); + Assert.Equal(expectedParamName, ((ArgumentException)exception).ParamName); + } + else + { + AssertExtensions.Throws(action); + } + } + else + { + AssertExtensions.Throws(expectedParamName, action); + } + } + + public static Exception Throws(Action action) + where TNetCoreExceptionType : Exception + where TNetFxExceptionType : Exception + { + return Throws(typeof(TNetCoreExceptionType), typeof(TNetFxExceptionType), action); + } + + public static Exception Throws(Type netCoreExceptionType, Type netFxExceptionType, Action action) + { + if (IsNetFramework) + { + return Assert.Throws(netFxExceptionType, action); + } + else + { + return Assert.Throws(netCoreExceptionType, action); + } + } + + public static void Throws(string netCoreParamName, string netFxParamName, Action action) + where TNetCoreExceptionType : ArgumentException + where TNetFxExceptionType : ArgumentException + { + if (IsNetFramework) + { + Throws(netFxParamName, action); + } + else + { + Throws(netCoreParamName, action); + } + } + + public static void ThrowsAny(Type firstExceptionType, Type secondExceptionType, Action action) + { + ThrowsAnyInternal(action, firstExceptionType, secondExceptionType); + } + + private static void ThrowsAnyInternal(Action action, params Type[] exceptionTypes) + { + try + { + action(); + } + catch (Exception e) + { + Type exceptionType = e.GetType(); + if (exceptionTypes.Any(t => t.Equals(exceptionType))) + return; + + throw new XunitException($"Expected one of: ({string.Join(", ", exceptionTypes)}) -> Actual: ({exceptionType}): {e}"); // Log message and callstack to help diagnosis + } + + throw new XunitException($"Expected one of: ({string.Join(", ", exceptionTypes)}) -> Actual: No exception thrown"); + } + + public static void ThrowsAny(Action action) + where TFirstExceptionType : Exception + where TSecondExceptionType : Exception + { + ThrowsAnyInternal(action, typeof(TFirstExceptionType), typeof(TSecondExceptionType)); + } + + public static void ThrowsAny(Action action) + where TFirstExceptionType : Exception + where TSecondExceptionType : Exception + where TThirdExceptionType : Exception + { + ThrowsAnyInternal(action, typeof(TFirstExceptionType), typeof(TSecondExceptionType), typeof(TThirdExceptionType)); + } + + public static void ThrowsIf(bool condition, Action action) + where T : Exception + { + if (condition) + { + Assert.Throws(action); + } + else + { + action(); + } + } + + public static void Canceled(CancellationToken cancellationToken, Action testCode) + { + OperationCanceledException oce = Assert.ThrowsAny(testCode); + if (cancellationToken.CanBeCanceled) + { + Assert.Equal(cancellationToken, oce.CancellationToken); + } + } + + public static Task CanceledAsync(CancellationToken cancellationToken, Task task) + { + Assert.NotNull(task); + return CanceledAsync(cancellationToken, () => task); } - public static void Equal(T[] expected, T[] actual) + public static async Task CanceledAsync(CancellationToken cancellationToken, Func testCode) { - if (expected is null) + OperationCanceledException oce = await Assert.ThrowsAnyAsync(testCode); + if (cancellationToken.CanBeCanceled) { - Assert.Null(actual); + Assert.Equal(cancellationToken, oce.CancellationToken); + } + } + + private static string AddOptionalUserMessage(string message, string userMessage) + { + if (userMessage is null) + return message; + else + return $"{message} {userMessage}"; + } + + /// + /// Tests whether the specified string contains the specified substring + /// and throws an exception if the substring does not occur within the + /// test string or if either string or substring is null. + /// + /// + /// The string that is expected to contain . + /// + /// + /// The string expected to occur within . + /// + public static void Contains(string value, string substring) + { + Assert.NotNull(value); + Assert.NotNull(substring); + Assert.Contains(substring, value, StringComparison.Ordinal); + } + + /// + /// Validate that a given value is greater than another value. + /// + /// The value that should be greater than . + /// The value that should be greater than. + public static void GreaterThan(T actual, T greaterThan, string userMessage = null) where T : IComparable + { + if (actual is null) + throw new XunitException( + greaterThan is null + ? AddOptionalUserMessage($"Expected: to be greater than .", userMessage) + : AddOptionalUserMessage($"Expected: to be greater than {greaterThan}.", userMessage)); + + if (actual.CompareTo(greaterThan) <= 0) + throw new XunitException(AddOptionalUserMessage($"Expected: {actual} to be greater than {greaterThan}", userMessage)); + } + + /// + /// Validate that a given value is less than another value. + /// + /// The value that should be less than . + /// The value that should be less than. + public static void LessThan(T actual, T lessThan, string userMessage = null) where T : IComparable + { + if (actual is null) + { + if (lessThan is null) + { + throw new XunitException(AddOptionalUserMessage($"Expected: to be less than .", userMessage)); + } + else + { + // Null is always less than non-null + return; + } + } + + if (actual.CompareTo(lessThan) >= 0) + throw new XunitException(AddOptionalUserMessage($"Expected: {actual} to be less than {lessThan}", userMessage)); + } + + /// + /// Validate that a given value is less than or equal to another value. + /// + /// The value that should be less than or equal to + /// The value that should be less than or equal to. + public static void LessThanOrEqualTo(T actual, T lessThanOrEqualTo, string userMessage = null) where T : IComparable + { + // null, by definition is always less than or equal to + if (actual is null) return; + + if (actual.CompareTo(lessThanOrEqualTo) > 0) + throw new XunitException(AddOptionalUserMessage($"Expected: {actual} to be less than or equal to {lessThanOrEqualTo}", userMessage)); + } + + /// + /// Validate that a given value is greater than or equal to another value. + /// + /// The value that should be greater than or equal to + /// The value that should be greater than or equal to. + public static void GreaterThanOrEqualTo(T actual, T greaterThanOrEqualTo, string userMessage = null) where T : IComparable + { + // null, by definition is always less than or equal to + if (actual is null) + { + if (greaterThanOrEqualTo is null) + { + // We're equal + return; + } + else + { + // Null is always less than non-null + throw new XunitException(AddOptionalUserMessage($"Expected: to be greater than or equal to .", userMessage)); + } + } + + if (actual.CompareTo(greaterThanOrEqualTo) < 0) + throw new XunitException(AddOptionalUserMessage($"Expected: {actual} to be greater than or equal to {greaterThanOrEqualTo}", userMessage)); + } + + // NOTE: Consider using SequenceEqual below instead, as it will give more useful information about what + // the actual differences are, especially for large arrays/spans. + /// + /// Validates that the actual array is equal to the expected array. XUnit only displays the first 5 values + /// of each collection if the test fails. This doesn't display at what point or how the equality assertion failed. + /// + /// The array that should be equal to. + /// + public static void Equal(T[] expected, T[] actual) where T : IEquatable + { + // Use the SequenceEqual to compare the arrays for better performance. The default Assert.Equal method compares + // the arrays by boxing each element that is very slow for large arrays. + if (!expected.AsSpan().SequenceEqual(actual.AsSpan())) + { + string expectedString = string.Join(", ", expected); + string actualString = string.Join(", ", actual); + throw new AssertActualExpectedException(expectedString, actualString, null); + } + } + + /// Validates that the two sets contains the same elements. XUnit doesn't display the full collections. + public static void Equal(HashSet expected, HashSet actual) + { + if (!actual.SetEquals(expected)) + { + throw new XunitException($"Expected: {string.Join(", ", expected)}{Environment.NewLine}Actual: {string.Join(", ", actual)}"); + } + } + + /// + /// Validates that the actual collection contains same items as expected collection. If the test fails, this will display: + /// 1. Count if two collection count are different; + /// 2. Missed expected collection item when found + /// + /// The collection that should contain same items as + /// + /// The comparer used to compare the items in two collections + public static void CollectionEqual(IEnumerable expected, IEnumerable actual, IEqualityComparer comparer) + { + var actualItemCountMapping = new Dictionary(comparer); + int actualCount = 0; + foreach (T actualItem in actual) + { + if (actualItemCountMapping.TryGetValue(actualItem, out ItemCount countInfo)) + { + countInfo.Original++; + countInfo.Remain++; + } + else + { + actualItemCountMapping[actualItem] = new ItemCount(1, 1); + } + + actualCount++; + } + + T[] expectedArray = expected.ToArray(); + int expectedCount = expectedArray.Length; + + if (expectedCount != actualCount) + { + throw new XunitException($"Expected count: {expectedCount}{Environment.NewLine}Actual count: {actualCount}"); + } + + for (int i = 0; i < expectedCount; i++) + { + T currentExpectedItem = expectedArray[i]; + if (!actualItemCountMapping.TryGetValue(currentExpectedItem, out ItemCount countInfo)) + { + throw new XunitException($"Expected: {currentExpectedItem} but not found"); + } + + if (countInfo.Remain == 0) + { + throw new XunitException($"Collections are not equal.{Environment.NewLine}Totally {countInfo.Original} {currentExpectedItem} in actual collection but expect more {currentExpectedItem}"); + } + + countInfo.Remain--; + } + } + + /// + /// Validates that the actual span is equal to the expected span. + /// If this fails, determine where the differences are and create an exception with that information. + /// + /// The array that should be equal to. + /// + public static void SequenceEqual(ReadOnlySpan expected, ReadOnlySpan actual) where T : IEquatable + { + // Use the SequenceEqual to compare the arrays for better performance. The default Assert.Equal method compares + // the arrays by boxing each element that is very slow for large arrays. + if (!expected.SequenceEqual(actual)) + { + if (expected.Length != actual.Length) + { + throw new XunitException($"Expected: Span of length {expected.Length}{Environment.NewLine}Actual: Span of length {actual.Length}"); + } + else + { + const int MaxDiffsToShow = 10; // arbitrary; enough to be useful, hopefully, but still manageable + + int diffCount = 0; + string message = $"Showing first {MaxDiffsToShow} differences{Environment.NewLine}"; + for (int i = 0; i < expected.Length; i++) + { + if (!expected[i].Equals(actual[i])) + { + diffCount++; + + // Add up to 10 differences to the exception message + if (diffCount <= MaxDiffsToShow) + { + message += $" Position {i}: Expected: {expected[i]}, Actual: {actual[i]}{Environment.NewLine}"; + } + } + } + + message += $"Total number of differences: {diffCount} out of {expected.Length}"; + + throw new XunitException(message); + } + } + } + + public static void FilledWith(T expected, ReadOnlySpan actual) + { + EqualityComparer comparer = EqualityComparer.Default; + + for (int i = 0; i < actual.Length; i++) + { + if (!comparer.Equals(expected, actual[i])) + { + throw new XunitException($"Expected {expected?.ToString() ?? "null"} at position {i}; actual {actual[i]?.ToString() ?? "null"}"); + } + } + } + + public static void SequenceEqual(Span expected, Span actual) where T : IEquatable => SequenceEqual((ReadOnlySpan)expected, (ReadOnlySpan)actual); + + public static void SequenceEqual(T[] expected, T[] actual) where T : IEquatable => SequenceEqual(expected.AsSpan(), actual.AsSpan()); + + public static void AtLeastOneEquals(T expected1, T expected2, T value) + { + EqualityComparer comparer = EqualityComparer.Default; + if (!(comparer.Equals(value, expected1) || comparer.Equals(value, expected2))) + throw new XunitException($"Expected: {expected1} || {expected2}{Environment.NewLine}Actual: {value}"); + } + + /// + /// Compares two strings, logs entire content if they are not equal. + /// + public static void Equal(string expected, string actual) + { + try + { + Assert.Equal(expected, actual); + } + catch (Exception e) + { + throw new XunitException( + e.Message + Environment.NewLine + + Environment.NewLine + + "Expected:" + Environment.NewLine + + expected + Environment.NewLine + + Environment.NewLine + + "Actual:" + Environment.NewLine + + actual + Environment.NewLine + + Environment.NewLine); + } + } + + public delegate void AssertThrowsActionReadOnly(ReadOnlySpan span); + + public delegate void AssertThrowsAction(Span span); + + // Cannot use standard Assert.Throws() when testing Span - Span and closures don't get along. + public static E AssertThrows(ReadOnlySpan span, AssertThrowsActionReadOnly action) where E : Exception + { + Exception exception; + + try + { + action(span); + exception = null; + } + catch (Exception ex) + { + exception = ex; + } + + switch(exception) + { + case null: + throw new ThrowsException(typeof(E)); + case E ex when (ex.GetType() == typeof(E)): + return ex; + default: + throw new ThrowsException(typeof(E), exception); + } + } + + public static E AssertThrows(Span span, AssertThrowsAction action) where E : Exception + { + Exception exception; + + try + { + action(span); + exception = null; + } + catch (Exception ex) + { + exception = ex; + } + + switch(exception) + { + case null: + throw new ThrowsException(typeof(E)); + case E ex when (ex.GetType() == typeof(E)): + return ex; + default: + throw new ThrowsException(typeof(E), exception); + } + } + + public static E Throws(string expectedParamName, ReadOnlySpan span, AssertThrowsActionReadOnly action) + where E : ArgumentException + { + E exception = AssertThrows(span, action); + Assert.Equal(expectedParamName, exception.ParamName); + return exception; + } + + public static E Throws(string expectedParamName, Span span, AssertThrowsAction action) + where E : ArgumentException + { + E exception = AssertThrows(span, action); + Assert.Equal(expectedParamName, exception.ParamName); + return exception; + } + + private class ItemCount + { + public int Original { get; set; } + public int Remain { get; set; } + + public ItemCount(int original, int remain) + { + Original = original; + Remain = remain; + } + } + + /// Verifies that two values are equal, within the . + /// The expected value + /// The value to be compared against + /// The total variance allowed between the expected and actual results. + /// Thrown when the values are not equal + public static void Equal(double expected, double actual, double variance) + { + if (double.IsNaN(expected)) + { + if (double.IsNaN(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (double.IsNaN(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (double.IsNegativeInfinity(expected)) + { + if (double.IsNegativeInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (double.IsNegativeInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (double.IsPositiveInfinity(expected)) + { + if (double.IsPositiveInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (double.IsPositiveInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (IsNegativeZero(expected)) + { + if (IsNegativeZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsNegativeZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + if (IsPositiveZero(expected)) + { + if (IsPositiveZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsPositiveZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + double delta = Math.Abs(actual - expected); + + if (delta > variance) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + static unsafe bool IsNegativeZero(double value) + { + return (*(ulong*)(&value)) == 0x8000000000000000; + } + + static unsafe bool IsPositiveZero(double value) + { + return (*(ulong*)(&value)) == 0x0000000000000000; + } + + // We have a custom ToString here to ensure that edge cases (specifically +-0.0, + // but also NaN and +-infinity) are correctly and consistently represented. + static string ToStringPadded(double value) + { + if (double.IsNaN(value)) + { + return "NaN".PadLeft(20); + } + else if (double.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(20); + } + else if (double.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(20); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(20); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(20); + } + else + { + return $"{value,20:G17}"; + } + } + } + + /// Verifies that two values are equal, within the . + /// The expected value + /// The value to be compared against + /// The total variance allowed between the expected and actual results. + /// Thrown when the values are not equal + public static void Equal(float expected, float actual, float variance) + { + if (float.IsNaN(expected)) + { + if (float.IsNaN(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (float.IsNaN(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (float.IsNegativeInfinity(expected)) + { + if (float.IsNegativeInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (float.IsNegativeInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (float.IsPositiveInfinity(expected)) + { + if (float.IsPositiveInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (float.IsPositiveInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (IsNegativeZero(expected)) + { + if (IsNegativeZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsNegativeZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + if (IsPositiveZero(expected)) + { + if (IsPositiveZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsPositiveZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + float delta = Math.Abs(actual - expected); + + if (delta > variance) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + static unsafe bool IsNegativeZero(float value) + { + return (*(uint*)(&value)) == 0x80000000; + } + + static unsafe bool IsPositiveZero(float value) + { + return (*(uint*)(&value)) == 0x00000000; } - Assert.NotNull(actual); + // We have a custom ToString here to ensure that edge cases (specifically +-0.0, + // but also NaN and +-infinity) are correctly and consistently represented. + static string ToStringPadded(float value) + { + if (float.IsNaN(value)) + { + return "NaN".PadLeft(10); + } + else if (float.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(10); + } + else if (float.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(10); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(10); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(10); + } + else + { + return $"{value,10:G9}"; + } + } + } + +#if NET6_0_OR_GREATER + /// Verifies that two values are equal, within the . + /// The expected value + /// The value to be compared against + /// The total variance allowed between the expected and actual results. + /// Thrown when the values are not equal + public static void Equal(Half expected, Half actual, Half variance) + { + if (Half.IsNaN(expected)) + { + if (Half.IsNaN(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (Half.IsNaN(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (Half.IsNegativeInfinity(expected)) + { + if (Half.IsNegativeInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (Half.IsNegativeInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (Half.IsPositiveInfinity(expected)) + { + if (Half.IsPositiveInfinity(actual)) + { + return; + } + + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + else if (Half.IsPositiveInfinity(actual)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + if (IsNegativeZero(expected)) + { + if (IsNegativeZero(actual)) + { + return; + } - Assert.Equal(expected.Length, actual.Length); + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsNegativeZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly -0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + if (IsPositiveZero(expected)) + { + if (IsPositiveZero(actual)) + { + return; + } + + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + else if (IsPositiveZero(actual)) + { + if (IsPositiveZero(variance) || IsNegativeZero(variance)) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + // When the variance is not +-0.0, then we are handling a case where + // the actual result is expected to not be exactly +0.0 on some platforms + // and we should fallback to checking if it is within the allowed variance instead. + } + + Half delta = (Half)Math.Abs((float)actual - (float)expected); + + if (delta > variance) + { + throw new EqualException(ToStringPadded(expected), ToStringPadded(actual)); + } + + static unsafe bool IsNegativeZero(Half value) + { + return (*(ushort*)(&value)) == 0x8000; + } + + static unsafe bool IsPositiveZero(Half value) + { + return (*(ushort*)(&value)) == 0x0000; + } - for (int i = 0; i < expected.Length; i++) + // We have a custom ToString here to ensure that edge cases (specifically +-0.0, + // but also NaN and +-infinity) are correctly and consistently represented. + static string ToStringPadded(Half value) { - Assert.Equal(expected[i], actual[i]); + if (Half.IsNaN(value)) + { + return "NaN".PadLeft(5); + } + else if (Half.IsPositiveInfinity(value)) + { + return "+\u221E".PadLeft(5); + } + else if (Half.IsNegativeInfinity(value)) + { + return "-\u221E".PadLeft(5); + } + else if (IsNegativeZero(value)) + { + return "-0.0".PadLeft(5); + } + else if (IsPositiveZero(value)) + { + return "+0.0".PadLeft(5); + } + else + { + return $"{value,5:G5}"; + } } } +#endif } } diff --git a/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.Windows.cs b/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.Windows.cs index 4cbbb68790d..332d76779ae 100644 --- a/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.Windows.cs +++ b/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.Windows.cs @@ -11,6 +11,7 @@ namespace System { public static partial class PlatformDetection { + public static bool IsNetFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase); public static Version OSXVersion => throw new PlatformNotSupportedException(); public static Version OpenSslVersion => throw new PlatformNotSupportedException(); public static bool IsSuperUser => throw new PlatformNotSupportedException(); @@ -92,6 +93,7 @@ public static bool IsWindowsHomeEdition public static bool IsWindows => true; public static bool IsWindows7 => GetWindowsVersion() == 6 && GetWindowsMinorVersion() == 1; + public static bool IsNotWindows7 => !IsWindows7; public static bool IsWindows8x => GetWindowsVersion() == 6 && (GetWindowsMinorVersion() == 2 || GetWindowsMinorVersion() == 3); public static string LibcRelease => "glibc_not_found"; diff --git a/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.cs b/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.cs index 7e10ab40c0e..cf269729c3a 100644 --- a/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.cs +++ b/src/System.Windows.Forms.Primitives/tests/TestUtilities/PlatformDetection.cs @@ -30,6 +30,19 @@ public static partial class PlatformDetection public static bool IsDrawingSupported => (IsNotWindowsNanoServer && IsNotWindowsIoTCore); public static bool IsArmProcess => RuntimeInformation.ProcessArchitecture == Architecture.Arm; public static bool IsNotArmProcess => !IsArmProcess; + public static bool IsArm64Process => RuntimeInformation.ProcessArchitecture == Architecture.Arm64; + public static bool IsNotArm64Process => !IsArm64Process; + public static bool IsArmOrArm64Process => IsArmProcess || IsArm64Process; + public static bool Is32BitProcess => IntPtr.Size == 4; + + public static bool IsMonoRuntime => Type.GetType("Mono.RuntimeStructs") is object; + public static bool IsNotMonoRuntime => !IsMonoRuntime; + public static bool IsMonoInterpreter => GetIsRunningOnMonoInterpreter(); + public static bool IsNativeAot => IsNotMonoRuntime && !IsReflectionEmitSupported; + + // Changed to `true` when trimming + public static bool IsBuiltWithAggressiveTrimming => IsNativeAot; + public static bool IsNotBuiltWithAggressiveTrimming => !IsBuiltWithAggressiveTrimming; public static bool IsNotInAppContainer => !IsInAppContainer; public static bool IsWinRTSupported => IsWindows && !IsWindows7; @@ -124,13 +137,27 @@ public static bool IsNonZeroLowerBoundArraySupported private static volatile Tuple s_lazyNonZeroLowerBoundArraySupported; - public static bool IsReflectionEmitSupported = !PlatformDetection.IsNetNative; - // Tracked in: https://github.com/dotnet/corert/issues/3643 in case we change our mind about this. public static bool IsInvokingStaticConstructorsSupported => !PlatformDetection.IsNetNative; // System.Security.Cryptography.Xml.XmlDsigXsltTransform.GetOutput() relies on XslCompiledTransform which relies // heavily on Reflection.Emit public static bool IsXmlDsigXsltTransformSupported => !PlatformDetection.IsUap; + + private static bool GetIsRunningOnMonoInterpreter() + { +#if NETCOREAPP + return IsMonoRuntime && RuntimeFeature.IsDynamicCodeSupported && !RuntimeFeature.IsDynamicCodeCompiled; +#else + return false; +#endif + } + +#if NETCOREAPP + public static bool IsReflectionEmitSupported => RuntimeFeature.IsDynamicCodeSupported; + public static bool IsNotReflectionEmitSupported => !IsReflectionEmitSupported; +#else + public static bool IsReflectionEmitSupported => true; +#endif } } diff --git a/src/System.Windows.Forms/src/System.Windows.Forms.csproj b/src/System.Windows.Forms/src/System.Windows.Forms.csproj index dbaa6670948..6ad4d72f90c 100644 --- a/src/System.Windows.Forms/src/System.Windows.Forms.csproj +++ b/src/System.Windows.Forms/src/System.Windows.Forms.csproj @@ -36,7 +36,6 @@ - diff --git a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/References.targets b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/References.targets index ad0160c815a..77f72132e45 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/References.targets +++ b/src/System.Windows.Forms/tests/IntegrationTests/MauiTests/References.targets @@ -1,9 +1,5 @@ - - - -