add Nvidia digital vibrance/hue control, add creation of custom color profile, add support for LG A2/A3 service menu unlocker

This commit is contained in:
Vincent Maas committed 2023-12-28 21:48:33 +01:00
1 parent 6b3b0b4b17
commit 5611c1bea7
57 files changed
+5277 -153

No files matched your search

+20
View File
@@ -15,6 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NLogViewer", "NLogViewer\NL
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "Shared\Shared.csproj", "{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LittleCms", "LittleCms\LittleCms.csproj", "{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MHC2Gen", "MHC2Gen\MHC2Gen.csproj", "{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -71,6 +75,22 @@ Global
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|Any CPU.Build.0 = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|x64.ActiveCfg = Release|Any CPU
{8F38D6B0-82DA-4DD1-89EB-918D8A18AEB1}.Release|x64.Build.0 = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|x64.ActiveCfg = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Debug|x64.Build.0 = Debug|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|Any CPU.Build.0 = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|x64.ActiveCfg = Release|Any CPU
{42D251AE-42AF-4C0D-B163-7C7A13C4A9A6}.Release|x64.Build.0 = Release|Any CPU
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|Any CPU.ActiveCfg = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|Any CPU.Build.0 = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|x64.ActiveCfg = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Debug|x64.Build.0 = Debug|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|Any CPU.ActiveCfg = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|Any CPU.Build.0 = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|x64.ActiveCfg = Release|x64
{9FDF741F-0ECF-46DB-8671-AB11ACEB9F41}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
+9 -3
View File
@@ -18,8 +18,8 @@
<PublisherName>Maassoft</PublisherName>
<Company>Maassoft</Company>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>9.7.2.0</ApplicationVersion>
<Version>9.7.2.0</Version>
<ApplicationVersion>9.8.0.0</ApplicationVersion>
<Version>9.8.0.0</Version>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>false</BootstrapperEnabled>
@@ -29,7 +29,7 @@
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<ApplicationIcon>pngbarn.ico</ApplicationIcon>
<SupportedOSPlatformVersion>10.0.20348.0</SupportedOSPlatformVersion>
<Copyright>2020-2023 Maassoft</Copyright>
<Copyright>2020-2024 Maassoft</Copyright>
<Authors>Maassoft</Authors>
<ManifestCertificateThumbprint>CF0BC153EA664CD97AC6F234B91F45D34630958F</ManifestCertificateThumbprint>
<ManifestKeyFile>ColorControl_1_TemporaryKey.pfx</ManifestKeyFile>
@@ -52,6 +52,7 @@
<PathMap Condition="'$(Configuration)' == 'Release'">$(MSBuildThisFileDirectory)=.\</PathMap>
</PropertyGroup>
<ItemGroup>
<None Remove="XForms\ColorProfileWindow.xaml" />
<None Remove="XForms\LogWindow.xaml" />
</ItemGroup>
<ItemGroup>
@@ -113,11 +114,16 @@
<PackageReference Include="RawInput.Sharp" Version="0.1.1" />
</ItemGroup>
<ItemGroup>
<Page Include="XForms\ColorProfileWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="XForms\LogWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MHC2Gen\MHC2Gen.csproj" />
<ProjectReference Include="..\NLogViewer\NLogViewer.csproj" />
<ProjectReference Include="..\novideo_srgb\novideo_srgb.csproj" />
<ProjectReference Include="..\Nspector\Nspector.csproj" />
+5 -7
View File
@@ -8,15 +8,12 @@ namespace ColorControl.Forms
public partial class InfoPanel : UserControl
{
private readonly WinApiService _winApiService;
private bool _initialized;
public InfoPanel(WinApiService winApiService)
{
_winApiService = winApiService;
InitializeComponent();
_initialized = true;
if (DarkModeUtils.UseDarkMode)
{
grpInfo.ForeColor = FormUtils.CurrentForeColor;
@@ -29,14 +26,14 @@ namespace ColorControl.Forms
{
var currentVersionInfo = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
Text = Application.ProductName + " " + Application.ProductVersion;
var text = Application.ProductName + " " + Application.ProductVersion;
if (_winApiService.IsAdministrator())
{
Text += " (administrator)";
text += " (administrator)";
}
lblInfo.Text = Text + " - " + currentVersionInfo.LegalCopyright;
lblInfo.Text = text + " - " + currentVersionInfo.LegalCopyright;
lbPlugins.Items.Add("lgtv.net by gr4b4z");
lbPlugins.Items.Add("Newtonsoft.Json by James Newton-King");
@@ -49,7 +46,8 @@ namespace ColorControl.Forms
lbPlugins.Items.Add("Novideo sRGB by ledoge");
lbPlugins.Items.Add("NLogViewer by dojo90");
lbPlugins.Items.Add("WPFDarkTheme by AngryCarrot789");
lbPlugins.Items.Add("Little-CMS by mm2");
lbPlugins.Items.Add("MHC2 by dantmnf");
}
}
+35
View File
@@ -28,6 +28,7 @@
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
tcMain = new System.Windows.Forms.TabControl();
tabOptions = new System.Windows.Forms.TabPage();
@@ -35,6 +36,7 @@
label1 = new System.Windows.Forms.Label();
chkModules = new System.Windows.Forms.CheckedListBox();
grpMiscellaneousOptions = new System.Windows.Forms.GroupBox();
btnOptionsColorProfiles = new System.Windows.Forms.Button();
btnSetShortcutScreenSaver = new System.Windows.Forms.Button();
label11 = new System.Windows.Forms.Label();
edtBlankScreenSaverShortcut = new System.Windows.Forms.TextBox();
@@ -60,11 +62,14 @@
chkStartAfterLogin = new System.Windows.Forms.CheckBox();
tabInfo = new System.Windows.Forms.TabPage();
btnUpdate = new System.Windows.Forms.Button();
mnuColorProfiles = new System.Windows.Forms.ContextMenuStrip(components);
miCreateHDRColorProfile = new System.Windows.Forms.ToolStripMenuItem();
tcMain.SuspendLayout();
tabOptions.SuspendLayout();
grpOptionsModules.SuspendLayout();
grpMiscellaneousOptions.SuspendLayout();
grpGeneralOptions.SuspendLayout();
mnuColorProfiles.SuspendLayout();
SuspendLayout();
//
// tcMain
@@ -130,6 +135,7 @@
//
// grpMiscellaneousOptions
//
grpMiscellaneousOptions.Controls.Add(btnOptionsColorProfiles);
grpMiscellaneousOptions.Controls.Add(btnSetShortcutScreenSaver);
grpMiscellaneousOptions.Controls.Add(label11);
grpMiscellaneousOptions.Controls.Add(edtBlankScreenSaverShortcut);
@@ -144,6 +150,17 @@
grpMiscellaneousOptions.TabStop = false;
grpMiscellaneousOptions.Text = "Miscellaneous";
//
// btnOptionsColorProfiles
//
btnOptionsColorProfiles.Location = new System.Drawing.Point(538, 17);
btnOptionsColorProfiles.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
btnOptionsColorProfiles.Name = "btnOptionsColorProfiles";
btnOptionsColorProfiles.Size = new System.Drawing.Size(112, 27);
btnOptionsColorProfiles.TabIndex = 12;
btnOptionsColorProfiles.Text = "Color Profiles...";
btnOptionsColorProfiles.UseVisualStyleBackColor = true;
btnOptionsColorProfiles.Click += btnOptionsColorProfiles_Click;
//
// btnSetShortcutScreenSaver
//
btnSetShortcutScreenSaver.Location = new System.Drawing.Point(181, 98);
@@ -455,6 +472,20 @@
btnUpdate.Visible = false;
btnUpdate.Click += btnUpdate_Click;
//
// mnuColorProfiles
//
mnuColorProfiles.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuColorProfiles.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miCreateHDRColorProfile });
mnuColorProfiles.Name = "mnuLgButtons";
mnuColorProfiles.Size = new System.Drawing.Size(214, 26);
//
// miCreateHDRColorProfile
//
miCreateHDRColorProfile.Name = "miCreateHDRColorProfile";
miCreateHDRColorProfile.Size = new System.Drawing.Size(213, 22);
miCreateHDRColorProfile.Text = "Create HDR Color Profile...";
miCreateHDRColorProfile.Click += miCreateHDRColorProfile_Click;
//
// MainForm
//
AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@@ -486,6 +517,7 @@
grpMiscellaneousOptions.PerformLayout();
grpGeneralOptions.ResumeLayout(false);
grpGeneralOptions.PerformLayout();
mnuColorProfiles.ResumeLayout(false);
ResumeLayout(false);
}
@@ -522,6 +554,9 @@
private System.Windows.Forms.CheckBox chkOptionsUseDarkMode;
private System.Windows.Forms.Button btnOptionsAdvanced;
private System.Windows.Forms.Button btnOptionsLog;
private System.Windows.Forms.Button btnOptionsColorProfiles;
private System.Windows.Forms.ContextMenuStrip mnuColorProfiles;
private System.Windows.Forms.ToolStripMenuItem miCreateHDRColorProfile;
}
}
+19 -1
View File
@@ -66,7 +66,6 @@ namespace ColorControl
private AmdPanel _amdPanel;
private bool _skipResize;
private FileVersionInfo _currentVersionInfo;
private bool _checkedForUpdates = false;
private string _updateHtmlUrl;
private string _downloadUrl;
@@ -96,6 +95,15 @@ namespace ColorControl
_dataDir = Program.DataDir;
_config = Program.Config;
var currentVersionInfo = FileVersionInfo.GetVersionInfo(Application.ExecutablePath);
Text = Application.ProductName + " " + Application.ProductVersion;
if (_winApiService.IsAdministrator())
{
Text += " (administrator)";
}
LoadConfig();
KeyboardShortcutManager.SetUseRawInput(_config.UseRawInput);
@@ -1423,5 +1431,15 @@ Currently ColorControl is {(_winApiService.IsAdministrator() ? "" : "not ")}runn
var module = _config.Modules[index];
module.IsActive = e.NewValue == CheckState.Checked;
}
private void miCreateHDRColorProfile_Click(object sender, EventArgs e)
{
ColorProfileWindow.CreateAndShow();
}
private void btnOptionsColorProfiles_Click(object sender, EventArgs e)
{
mnuColorProfiles.ShowCustom(btnOptionsColorProfiles);
}
}
}
+3
View File
@@ -117,6 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="mnuColorProfiles.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>147, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
+1
View File
@@ -256,6 +256,7 @@ namespace ColorControl
services.AddSingleton<MainForm>();
services.AddSingleton<ElevatedForm>();
services.AddSingleton<LogWindow>();
services.AddTransient<ColorProfileWindow>();
services.AddSingleton<RestartDetector>();
services.AddSingleton<InfoPanel>();
});
@@ -77,7 +77,14 @@ namespace ColorControl.Services.GameLauncher
if (_winApiService.IsAdministrator())
{
var fileName = process.MainModule.FileName;
var mainModule = process.MainModule;
if (mainModule == null)
{
return false;
}
var fileName = mainModule.FileName;
preset = _presets.FirstOrDefault(p => fileName.Contains(Path.GetDirectoryName(p.Path), System.StringComparison.OrdinalIgnoreCase));
}
+1 -1
View File
@@ -829,7 +829,7 @@ Do you want to continue?"
mnuLgOLEDMotionPro.Visible = visible;
miLgEnableMotionPro.Visible = enableVisible;
var svcMenuFlagVisible = new[] { "B2", "C2", "G2", "B3", "C3", "G3" }.Any(m => device?.ModelName?.Contains(m) == true);
var svcMenuFlagVisible = new[] { "A2", "B2", "C2", "G2", "A3", "B3", "C3", "G3" }.Any(m => device?.ModelName?.Contains(m) == true);
mnuLgSetSvcMenuFlag.Visible = svcMenuFlagVisible;
+23 -5
View File
@@ -57,6 +57,8 @@ namespace ColorControl.Services.NVIDIA
miNvPrimaryDisplay = new System.Windows.Forms.ToolStripMenuItem();
mnuNvPresetsColorSettings = new System.Windows.Forms.ToolStripMenuItem();
miNvPresetColorSettings = new System.Windows.Forms.ToolStripMenuItem();
mnuNvPresetsColorEnhancements = new System.Windows.Forms.ToolStripMenuItem();
miNvPresetColorEnhancementsIncluded = new System.Windows.Forms.ToolStripMenuItem();
mnuRefreshRate = new System.Windows.Forms.ToolStripMenuItem();
miRefreshRateIncluded = new System.Windows.Forms.ToolStripMenuItem();
mnuNvResolution = new System.Windows.Forms.ToolStripMenuItem();
@@ -94,8 +96,8 @@ namespace ColorControl.Services.NVIDIA
miNvNovideo = new System.Windows.Forms.ToolStripMenuItem();
miNvSettings = new System.Windows.Forms.ToolStripMenuItem();
miNvTestDithering = new System.Windows.Forms.ToolStripMenuItem();
lvNvPresetsToolTip = new System.Windows.Forms.ToolTip(components);
miNVIDIAInfo = new System.Windows.Forms.ToolStripMenuItem();
lvNvPresetsToolTip = new System.Windows.Forms.ToolTip(components);
mnuNvPresets.SuspendLayout();
mnuNvSettings.SuspendLayout();
SuspendLayout();
@@ -326,9 +328,9 @@ namespace ColorControl.Services.NVIDIA
// mnuNvPresets
//
mnuNvPresets.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuNvPresets.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvApply, miNvPresetApplyOnStartup, tssNvPresetMenu, mnuNvDisplay, mnuNvPresetsColorSettings, mnuRefreshRate, mnuNvResolution, miNvPresetDithering, miNvHDR, mnuNvDriverSettings, mnuNvOtherSettings, mnuNvHdmiSettings, mnuNvOverclocking, miNvCopyId });
mnuNvPresets.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvApply, miNvPresetApplyOnStartup, tssNvPresetMenu, mnuNvDisplay, mnuNvPresetsColorSettings, mnuNvPresetsColorEnhancements, mnuRefreshRate, mnuNvResolution, miNvPresetDithering, miNvHDR, mnuNvDriverSettings, mnuNvOtherSettings, mnuNvHdmiSettings, mnuNvOverclocking, miNvCopyId });
mnuNvPresets.Name = "mnuNvPresets";
mnuNvPresets.Size = new System.Drawing.Size(185, 296);
mnuNvPresets.Size = new System.Drawing.Size(185, 340);
mnuNvPresets.Opening += mnuNvPresets_Opening;
//
// miNvApply
@@ -375,10 +377,24 @@ namespace ColorControl.Services.NVIDIA
// miNvPresetColorSettings
//
miNvPresetColorSettings.Name = "miNvPresetColorSettings";
miNvPresetColorSettings.Size = new System.Drawing.Size(120, 22);
miNvPresetColorSettings.Size = new System.Drawing.Size(180, 22);
miNvPresetColorSettings.Text = "Included";
miNvPresetColorSettings.Click += miNvPresetColorSettings_Click;
//
// mnuNvPresetsColorEnhancements
//
mnuNvPresetsColorEnhancements.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvPresetColorEnhancementsIncluded });
mnuNvPresetsColorEnhancements.Name = "mnuNvPresetsColorEnhancements";
mnuNvPresetsColorEnhancements.Size = new System.Drawing.Size(184, 22);
mnuNvPresetsColorEnhancements.Text = "Color Enhancements";
//
// miNvPresetColorEnhancementsIncluded
//
miNvPresetColorEnhancementsIncluded.Name = "miNvPresetColorEnhancementsIncluded";
miNvPresetColorEnhancementsIncluded.Size = new System.Drawing.Size(180, 22);
miNvPresetColorEnhancementsIncluded.Text = "Included";
miNvPresetColorEnhancementsIncluded.Click += miNvPresetColorEnhancementsIncluded_Click;
//
// mnuRefreshRate
//
mnuRefreshRate.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { miRefreshRateIncluded });
@@ -638,7 +654,7 @@ namespace ColorControl.Services.NVIDIA
mnuNvSettings.ImageScalingSize = new System.Drawing.Size(20, 20);
mnuNvSettings.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { miNvProfileInspector, miNvNovideo, miNvSettings, miNvTestDithering, miNVIDIAInfo });
mnuNvSettings.Name = "mnuLgButtons";
mnuNvSettings.Size = new System.Drawing.Size(202, 136);
mnuNvSettings.Size = new System.Drawing.Size(202, 114);
//
// miNvProfileInspector
//
@@ -778,5 +794,7 @@ namespace ColorControl.Services.NVIDIA
private System.Windows.Forms.ToolStripMenuItem miNvHdmiIncluded;
private System.Windows.Forms.ToolStripMenuItem miNvTestDithering;
private System.Windows.Forms.ToolStripMenuItem miNVIDIAInfo;
private System.Windows.Forms.ToolStripMenuItem mnuNvPresetsColorEnhancements;
private System.Windows.Forms.ToolStripMenuItem miNvPresetColorEnhancementsIncluded;
}
}
+84 -11
View File
@@ -337,6 +337,8 @@ namespace ColorControl.Services.NVIDIA
mnuNvDisplay.Visible = presetItemsVisible;
miNvPresetColorSettings.Enabled = presetItemsVisible;
mnuNvPresetsColorSettings.Enabled = preset != null;
miNvPresetColorEnhancementsIncluded.Enabled = presetItemsVisible;
mnuNvPresetsColorEnhancements.Enabled = preset != null;
mnuRefreshRate.Enabled = preset != null;
mnuNvResolution.Enabled = preset != null;
miNvPresetDithering.Enabled = preset != null;
@@ -363,6 +365,7 @@ namespace ColorControl.Services.NVIDIA
miNvPresetApplyDithering.Visible = !isCurrentDisplay;
miNvPresetColorSettings.Visible = !isCurrentDisplay;
miNvPresetColorEnhancementsIncluded.Visible = !isCurrentDisplay;
miNvResolutionIncluded.Visible = !isCurrentDisplay;
miRefreshRateIncluded.Visible = !isCurrentDisplay;
miHDRIncluded.Visible = !isCurrentDisplay;
@@ -422,8 +425,10 @@ namespace ColorControl.Services.NVIDIA
miNvOtherIncluded.Checked = preset.applyOther;
miNvHdmiIncluded.Checked = preset.applyHdmiSettings;
miNvDriverSettingsIncluded.Checked = preset.applyDriverSettings;
miNvPresetColorEnhancementsIncluded.Checked = preset.ApplyColorEnhancements;
mnuNvPresetsColorSettings.Font = _menuItemFonts[preset.applyColorData];
mnuNvPresetsColorEnhancements.Font = _menuItemFonts[preset.ApplyColorEnhancements];
mnuRefreshRate.Font = _menuItemFonts[preset.applyRefreshRate];
mnuNvResolution.Font = _menuItemFonts[preset.applyResolution];
miNvPresetDithering.Font = _menuItemFonts[preset.applyDithering];
@@ -432,22 +437,19 @@ namespace ColorControl.Services.NVIDIA
mnuNvOtherSettings.Font = _menuItemFonts[preset.applyOther];
mnuNvHdmiSettings.Font = _menuItemFonts[preset.applyHdmiSettings];
var itemName = $"{mnuNvOtherSettings.Name}_SDRBrightness";
var brightnessMenu = FormUtils.BuildMenuItem(mnuNvOtherSettings.DropDownItems, itemName, "SDR brightness");
FormUtils.BuildMenuItemAndSub(mnuNvPresetsColorEnhancements, "Digital Vibrance",
$"{preset.ColorEnhancementSettings.DigitalVibranceLevel}% (click to change)", nvPresetDvcMenuItem_Click);
FormUtils.BuildMenuItemAndSub(mnuNvPresetsColorEnhancements, "Hue",
$"{preset.ColorEnhancementSettings.HueAngle}° (click to change)", nvPresetHueMenuItem_Click);
var subItemName = $"{itemName}_SubItem";
var subItem = FormUtils.BuildMenuItem(brightnessMenu.DropDownItems, subItemName, "", onClick: nvPresetBrightnessMenuItem_Click);
subItem.Text = (preset.SDRBrightness.HasValue ? $"{preset.SDRBrightness.Value}%" : "Unset") + " (click to change)";
FormUtils.BuildMenuItemAndSub(mnuNvOtherSettings, "SDR brightness",
(preset.SDRBrightness.HasValue ? $"{preset.SDRBrightness.Value}%" : "Unset") + " (click to change)", nvPresetBrightnessMenuItem_Click);
FormUtils.BuildDropDownMenu(mnuNvOtherSettings, "Scaling", typeof(Scaling), preset, "scaling", nvPresetScalingMenuItem_Click, unchanged: true, skipValues: new object[] { Scaling.Customized })
.DropDownItems[0].Visible = !isCurrentDisplay;
var itemNameProfile = $"{mnuNvOtherSettings.Name}_Profile";
var profileMenu = FormUtils.BuildMenuItem(mnuNvOtherSettings.DropDownItems, itemNameProfile, "Color Profile");
var subItemNameProfile = $"{itemNameProfile}_SubItem";
var subItemProfile = FormUtils.BuildMenuItem(profileMenu.DropDownItems, subItemNameProfile, "", onClick: nvPresetColorProfileMenuItem_Click);
subItemProfile.Text = (!preset.ColorProfileSettings.ProfileName.IsNullOrWhiteSpace() ? $"{preset.ColorProfileSettings.ProfileName}" : "Unset") + " (click to change)";
FormUtils.BuildMenuItemAndSub(mnuNvOtherSettings, "Color Profile",
(!preset.ColorProfileSettings.ProfileName.IsNullOrWhiteSpace() ? $"{preset.ColorProfileSettings.ProfileName}" : "Unset") + " (click to change)", nvPresetColorProfileMenuItem_Click);
FormUtils.BuildDropDownMenu(mnuNvHdmiSettings, "Content type", typeof(InfoFrameVideoContentType), preset.HdmiInfoFrameSettings, "ContentType", nvPresetHdmiContentTypeMenuItem_Click, unchanged: true)
.DropDownItems[0].Visible = !isCurrentDisplay;
@@ -833,6 +835,68 @@ namespace ColorControl.Services.NVIDIA
AddOrUpdateItem();
}
private void nvPresetDvcMenuItem_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset(true);
var label = "Digital vibrance in % (clear to set to default)";
var display = _nvService.ResolveDisplay(preset);
var dvControl = display.DigitalVibranceControl;
var value = SelectValue(dvControl.MinimumLevel, dvControl.MaximumLevel, preset.ColorEnhancementSettings.DigitalVibranceLevel, label);
if (value == -1)
{
return;
}
var intValue = value ?? dvControl.DefaultLevel;
if (preset.IsDisplayPreset)
{
_nvService.SetDigitalVibranceLevel(preset.Display, intValue);
UpdateDisplayInfoItems();
return;
}
preset.ColorEnhancementSettings.DigitalVibranceLevel = intValue;
AddOrUpdateItem();
}
private void nvPresetHueMenuItem_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset(true);
var label = "Hue angle in ° (clear to set to default)";
var display = _nvService.ResolveDisplay(preset);
var hueControl = display.HUEControl;
var value = SelectValue(hueControl.DefaultAngle, 359, preset.ColorEnhancementSettings.HueAngle, label);
if (value == -1)
{
return;
}
var intValue = value ?? hueControl.DefaultAngle;
if (preset.IsDisplayPreset)
{
_nvService.SetHueAngle(preset.Display, intValue);
UpdateDisplayInfoItems();
return;
}
preset.ColorEnhancementSettings.HueAngle = intValue;
AddOrUpdateItem();
}
private void nvPresetScalingMenuItem_Click(object sender, EventArgs e)
{
var menuItem = (ToolStripMenuItem)sender;
@@ -1931,5 +1995,14 @@ namespace ColorControl.Services.NVIDIA
MessageForms.ShowControl(panel, "NVIDIA Info", height: 800);
}
private void miNvPresetColorEnhancementsIncluded_Click(object sender, EventArgs e)
{
var preset = GetSelectedNvPreset();
preset.ApplyColorEnhancements = !preset.ApplyColorEnhancements;
AddOrUpdateItem();
}
}
}
+1 -1
View File
@@ -18,7 +18,7 @@
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing"">Blue</data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
+31 -1
View File
@@ -20,6 +20,8 @@ namespace ColorControl.Services.NVIDIA
public bool applyColorData { get; set; }
public ColorData colorData { get; set; }
public bool ApplyColorEnhancements { get; set; }
public NvColorEnhancementSettings ColorEnhancementSettings { get; set; }
public bool applyHDR { get; set; }
public bool HDREnabled { get; set; }
public bool toggleHDR { get; set; }
@@ -77,6 +79,7 @@ namespace ColorControl.Services.NVIDIA
ocSettings = new List<NvGpuOcSettings>();
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings();
ColorProfileSettings = new NvColorProfileSettings();
ColorEnhancementSettings = new NvColorEnhancementSettings();
}
public NvPreset(ColorData colorData) : this()
@@ -116,6 +119,9 @@ namespace ColorControl.Services.NVIDIA
HdmiInfoFrameSettings = new NvHdmiInfoFrameSettings(preset.HdmiInfoFrameSettings);
applyHdmiSettings = preset.applyHdmiSettings;
ColorEnhancementSettings = new NvColorEnhancementSettings(preset.ColorEnhancementSettings);
ApplyColorEnhancements = preset.ApplyColorEnhancements;
}
public NvPreset Clone()
@@ -127,7 +133,7 @@ namespace ColorControl.Services.NVIDIA
public static string[] GetColumnNames()
{
return new[] { "Name", "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Refresh rate|100", "Resolution|120", "Dithering", "HDR", "Driver settings|300", "HDMI settings|200", "Other|200", "Overclocking|300", "Shortcut", "Apply on startup" };
return new[] { "Name", "Display|140", "Color settings (BPC, format, dyn. range, color space)|260", "Color enhancements", "Refresh rate|100", "Resolution|120", "Dithering", "HDR", "Driver settings|300", "HDMI settings|200", "Other|200", "Overclocking|300", "Shortcut", "Apply on startup" };
}
public override List<string> GetDisplayValues(Config config = null)
@@ -145,6 +151,9 @@ namespace ColorControl.Services.NVIDIA
var colorSettings = FormatDisplaySetting(string.Format("{0}, {1}, {2}, {3}", colorData.ColorDepth, colorData.ColorFormat, colorData.DynamicRange, colorData.Colorimetry), isCurrent, applyColorData);
values.Add(colorSettings);
values.Add(FormatDisplaySetting(GetColorEnhancementsDescription(), isCurrent, ApplyColorEnhancements));
values.Add(FormatDisplaySetting(string.Format("{0}Hz", refreshRate), isCurrent, applyRefreshRate));
if (applyResolution || resolutionWidth > 0)
@@ -298,6 +307,27 @@ namespace ColorControl.Services.NVIDIA
return string.Join(", ", values);
}
public string GetColorEnhancementsDescription()
{
var values = new List<string>();
if (ColorEnhancementSettings.DigitalVibranceLevel != 50)
{
values.Add($"Digital vibrance: {ColorEnhancementSettings.DigitalVibranceLevel}%");
}
if (ColorEnhancementSettings.HueAngle != 0)
{
values.Add($"Hue: {ColorEnhancementSettings.HueAngle}°");
}
if (!values.Any())
{
values.Add("None");
}
return string.Join(", ", values);
}
public static ColorData DefaultColorData => new ColorData(ColorDataFormat.Auto, dynamicRange: ColorDataDynamicRange.Auto, colorDepth: ColorDataDepth.Default, colorSelectionPolicy: ColorDataSelectionPolicy.BestQuality);
public static List<NvPreset> GetDefaultPresets()
+42 -4
View File
@@ -398,6 +398,12 @@ namespace ColorControl.Services.NVIDIA
}
}
if (preset.ApplyColorEnhancements)
{
SetDigitalVibranceLevel(display, preset.ColorEnhancementSettings.DigitalVibranceLevel);
SetHueAngle(display, preset.ColorEnhancementSettings.HueAngle);
}
if (preset.applyDriverSettings)
{
SetDriverSettings(preset.driverSettings);
@@ -609,6 +615,28 @@ namespace ColorControl.Services.NVIDIA
return 0;
}
public void SetDigitalVibranceLevel(Display display, int level)
{
var dvControl = display.DigitalVibranceControl;
dvControl.CurrentLevel = level;
}
public int GetDigitalVibranceLevel(Display display)
{
return display.DigitalVibranceControl.CurrentLevel;
}
public void SetHueAngle(Display display, int angle)
{
display.HUEControl.CurrentAngle = angle;
}
public int GetHueAngle(Display display)
{
return display.HUEControl.CurrentAngle;
}
public bool SetDithering(NvDitherState state, uint bits = 1, uint mode = 4, NvPreset preset = null, Display currentDisplay = null)
{
var result = true;
@@ -1132,6 +1160,7 @@ namespace ColorControl.Services.NVIDIA
preset.displayName = FormUtils.ExtendedDisplayName(display.Name);
preset.colorData = GetCurrentColorData(display);
preset.ColorEnhancementSettings = GetCurrentColorEnhancements(display);
preset.HdmiInfoFrameSettings = GetHdmiSettings(display);
preset.SDRBrightness = GetSDRBrightness(display);
preset.scaling = GetScaling(display);
@@ -1199,6 +1228,15 @@ namespace ColorControl.Services.NVIDIA
return preset;
}
private NvColorEnhancementSettings GetCurrentColorEnhancements(Display display)
{
return new NvColorEnhancementSettings
{
DigitalVibranceLevel = GetDigitalVibranceLevel(display),
HueAngle = GetHueAngle(display)
};
}
public List<SettingItem> GetVisibleSettings()
{
return GetSettings().Where(s => _driverSettingIds.Contains(s.SettingId)).ToList();
@@ -1368,14 +1406,14 @@ namespace ColorControl.Services.NVIDIA
Utils.WriteObject(_configFilename, Config);
}
internal void ResolveDisplay(NvPreset preset)
internal Display ResolveDisplay(NvPreset preset)
{
if (preset.Display != null)
if (preset.Display == null)
{
return;
preset.Display = GetPresetDisplay(preset);
}
preset.Display = GetPresetDisplay(preset);
return preset.Display;
}
}
}
@@ -0,0 +1,260 @@
using ColorControl.Shared.Common;
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using ColorControl.Shared.XForms;
using MHC2Gen;
using novideo_srgb;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Windows.Forms;
using WindowsDisplayAPI;
namespace ColorControl.XForms;
public enum SaveOption
{
Install,
[Description("Install and set as default")]
InstallAndSetAsDefault,
[Description("Save to file")]
SaveToFile
}
public enum DisplayPrimariesSource
{
Custom,
EDID,
Windows
}
public class CustomDisplay
{
public Display Display { get; set; }
public string DisplayName => Display.DisplayName;
public string DevicePath => Display.DevicePath;
public bool IsHDRSupportedAndEnabled()
{
var advancedColor = CCD.GetAdvancedColorSupportedAndEnabled(DisplayName);
return advancedColor.isSupported && advancedColor.isEnabled;
}
public CustomDisplay(Display display)
{
Display = display;
}
public override string ToString()
{
return $"{FormUtils.ExtendedDisplayName(Display.DisplayName)} on {Display.Adapter.DeviceName}";
}
}
public class ColorPointModel : BaseViewModel
{
private double _x;
public double X { get => _x; set { _x = value; OnPropertyChanged(); } }
private double _y;
public double Y { get => _y; set { _y = value; OnPropertyChanged(); } }
public ColorPointModel()
{
}
public ColorPointModel(CIExy coord)
{
X = coord.x;
Y = coord.y;
}
}
internal class ColorProfileViewModel : BaseViewModel
{
[StringLength(30)]
public string Description { get; set; }
public ColorPointModel RedPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Red);
public ColorPointModel GreenPoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Green);
public ColorPointModel BluePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.Blue);
public ColorPointModel WhitePoint { get; set; } = new ColorPointModel(RgbPrimaries.sRGB.White);
public Dictionary<DisplayPrimariesSource, string> PrimariesSources { get; } = Utils.EnumToDictionary<DisplayPrimariesSource>();
public DisplayPrimariesSource PrimariesSource { get; set; } = DisplayPrimariesSource.EDID;
public Dictionary<ColorGamut, string> ColorGamuts { get; } = Utils.EnumToDictionary<ColorGamut>();
public ColorGamut ColorGamut { get; set; } = ColorGamut.Native;
public Dictionary<SDRTransferFunction, string> SDRTransferFunctions { get; } = Utils.EnumToDictionary<SDRTransferFunction>();
public SDRTransferFunction SDRTransferFunction { get; set; } = SDRTransferFunction.PurePower;
[Range(0.1, 10)]
public double CustomGamma { get; set; } = 2.20;
[Range(0, 10)]
public double MinCLL { get; set; } = 0;
[Range(11, 10000)]
public double MaxCLL { get; set; } = 1000;
[Range(0, 10)]
public double BlackLuminance { get; set; } = 0;
[Range(11, 10000)]
public double WhiteLuminance { get; set; } = 1000;
[Range(0, 10)]
public double SDRMinBrightness { get; set; } = 0;
[Range(11, 1000)]
public double SDRMaxBrightness { get; set; } = 100;
[Range(-50, 50)]
public double SDRBrightnessBoost { get; set; }
public CustomDisplay SelectedDisplay { get; set; }
public IEnumerable<CustomDisplay> Displays { get; set; }
public string SelectedDisplayName => SelectedDisplay?.DisplayName;
public List<string> ExistingProfiles { get; set; }
public string SelectedExistingProfile { get; set; } = CreateANewProfile;
public string NewProfileName { get; set; }
public string ProfileName => SelectedExistingProfile == CreateANewProfile ? NewProfileName : SelectedExistingProfile;
public bool IsHDR { get; set; } = true;
public Dictionary<SaveOption, string> SaveOptions { get; } = Utils.EnumToDictionary<SaveOption>();
public SaveOption SaveOption { get; set; } = SaveOption.InstallAndSetAsDefault;
public override string this[string columnName]
{
get
{
var baseResult = base[columnName];
if (columnName == "SelectedDisplay" || columnName == "PrimariesSource")
{
UpdateDisplay();
}
return baseResult;
}
}
public override bool PreValidate()
{
return base.PreValidate() && Displays.Any();
}
public const string CreateANewProfile = "<create a new profile>";
public ColorProfileViewModel(bool isHDR = true)
{
IsHDR = isHDR;
RefreshDisplays();
ExistingProfiles = new List<string> { CreateANewProfile };
NewProfileName = $"CC_{(isHDR ? "HDR" : "SDR")}_profile_{DateTime.Now:yyyyMMddHHmmss}.icm";
Description = $"CC_{(isHDR ? "HDR" : "SDR")}";
if (Displays.Any())
{
SelectedDisplay = Displays.First();
UpdateDisplay();
}
}
public void RefreshDisplays()
{
Displays = Display.GetDisplays().Select(d => new CustomDisplay(d)).ToList();
if (IsHDR)
{
Displays = Displays.Where(d => d.IsHDRSupportedAndEnabled()).ToList();
}
}
private void UpdateDisplay()
{
if (SelectedDisplay == null)
{
return;
}
var colorParams = CCD.GetColorParams(SelectedDisplay.DisplayName);
if (colorParams.MaxLuminance > 0)
{
if (PrimariesSource == DisplayPrimariesSource.Windows)
{
var divider = colorParams.RedPointX <= 1 << 10 ? 1 << 10 : 1 << 20;
RedPoint.X = (double)colorParams.RedPointX / divider;
RedPoint.Y = (double)colorParams.RedPointY / divider;
GreenPoint.X = (double)colorParams.GreenPointX / divider;
GreenPoint.Y = (double)colorParams.GreenPointY / divider;
BluePoint.X = (double)colorParams.BluePointX / divider;
BluePoint.Y = (double)colorParams.BluePointY / divider;
WhitePoint.X = (double)colorParams.WhitePointX / divider;
WhitePoint.Y = (double)colorParams.WhitePointY / divider;
}
WhiteLuminance = (double)colorParams.MaxLuminance / 10000;
OnPropertyChanged(nameof(WhiteLuminance));
BlackLuminance = (double)colorParams.MinLuminance / 10000;
OnPropertyChanged(nameof(BlackLuminance));
MinCLL = BlackLuminance;
OnPropertyChanged(nameof(MinCLL));
MaxCLL = WhiteLuminance;
OnPropertyChanged(nameof(MaxCLL));
}
if (PrimariesSource == DisplayPrimariesSource.EDID)
{
GetEDID();
}
if (!CCD.GetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName))
{
if (MessageForms.QuestionYesNo("To be able to select/activate a color profile, user specific settings have to be enabled. Do you want to enable this?") == DialogResult.Yes)
{
CCD.SetUsePerUserDisplayProfiles(SelectedDisplay.DisplayName, true);
}
}
ExistingProfiles = new List<string> { CreateANewProfile };
ExistingProfiles.AddRange(CCD.GetDisplayColorProfiles(SelectedDisplay.DisplayName));
}
private void GetEDID()
{
if (SelectedDisplay == null)
{
return;
}
var path = SelectedDisplay.DevicePath;
var edid = Novideo.GetEDID(path, null);
var primaries = edid?.DisplayParameters?.ChromaticityCoordinates;
if (primaries == null)
{
return;
}
RedPoint.X = primaries.RedX;
RedPoint.Y = primaries.RedY;
GreenPoint.X = primaries.GreenX;
GreenPoint.Y = primaries.GreenY;
BluePoint.X = primaries.BlueX;
BluePoint.Y = primaries.BlueY;
WhitePoint.X = primaries.WhiteX;
WhitePoint.Y = primaries.WhiteY;
}
internal RgbPrimaries GetDevicePrimaries()
{
return new RgbPrimaries(
new CIExy { x = RedPoint.X, y = RedPoint.Y },
new CIExy { x = GreenPoint.X, y = GreenPoint.Y },
new CIExy { x = BluePoint.X, y = BluePoint.Y },
new CIExy { x = WhitePoint.X, y = WhitePoint.Y });
}
}
+104
View File
@@ -0,0 +1,104 @@
<src:BaseWindow x:Class="ColorControl.XForms.ColorProfileWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="Create Color Profile"
ResizeMode="CanResize"
Width="773"
Height="775"
WindowStartupLocation="CenterOwner"
SnapsToDevicePixels="True"
Style="{DynamicResource CustomWindowStyle}"
xmlns:src="clr-namespace:ColorControl.Shared.XForms;assembly=Shared" Loaded="Window_Loaded">
<StackPanel Margin="6,6,6,6" Width="733">
<GroupBox DockPanel.Dock="Top" Margin="0,0,0,0" Header="General Profile Settings" Padding="6" Height="287" Width="733" HorizontalAlignment="Left">
<Grid Height="270" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="132*"/>
<ColumnDefinition Width="577*"/>
</Grid.ColumnDefinitions>
<Label Content="Maximum Luminance (nits):" HorizontalAlignment="Left" Margin="206,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="412,236,0,0" TextWrapping="Wrap" Text="{Binding WhiteLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" Name="tbMaxLuminance" Height="18" InputScope="Number"/>
<Label Content="Minimum Luminance (nits):" HorizontalAlignment="Left" Margin="-5,232,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,236,0,0" TextWrapping="Wrap" Text="{Binding BlackLuminance, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance" Height="18" InputScope="Number"/>
<Label Content="Display:" HorizontalAlignment="Left" Margin="-6,8,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,10,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" SelectedValue="{Binding SelectedDisplay, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" ItemsSource="{Binding Displays}"/>
<Label Content="Color Gamut:" HorizontalAlignment="Left" Margin="-5,201,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,203,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding ColorGamuts}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding ColorGamut}" />
<Label Content="Display Primaries Source:" HorizontalAlignment="Left" Margin="-5,39,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="76,41,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="24" ItemsSource="{Binding PrimariesSources}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding PrimariesSource, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" />
<Label Content="Custom Primaries:" HorizontalAlignment="Left" Margin="-5,74,0,0" VerticalAlignment="Top" Width="118" Height="26"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Red:" HorizontalAlignment="Left" Margin="20,74,0,0" VerticalAlignment="Top" Width="51" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,78,0,0" TextWrapping="Wrap" Text="{Binding RedPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy1" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,74,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,74,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy2" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Green:" HorizontalAlignment="Left" Margin="20,105,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,109,0,0" TextWrapping="Wrap" Text="{Binding GreenPoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy3" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,105,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,105,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy4" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="Blue:" HorizontalAlignment="Left" Margin="19,136,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,140,0,0" TextWrapping="Wrap" Text="{Binding BluePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy5" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,136,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,136,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="76,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.X, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy7" Height="18" InputScope="Number"/>
<Label HorizontalContentAlignment="Right" Content="White:" HorizontalAlignment="Left" Margin="19,167,0,0" VerticalAlignment="Top" Width="52" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="195,171,0,0" TextWrapping="Wrap" Text="{Binding WhitePoint.Y, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="83" x:Name="tbSDRMinBrightness_Copy8" Height="18" InputScope="Number"/>
<Label Content="x" HorizontalAlignment="Left" Margin="164,167,0,0" VerticalAlignment="Top" Width="26" Height="26" Grid.Column="1"/>
<Label Content="y" HorizontalAlignment="Center" Margin="0,167,0,0" VerticalAlignment="Top" Width="24" Height="26" Grid.Column="1"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced HDR Settings" Padding="6" Height="133" Width="734" HorizontalAlignment="Left">
<Grid Height="103">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="179*"/>
<ColumnDefinition Width="735*"/>
</Grid.ColumnDefinitions>
<Label Content="SDR Transfer Function &amp; Gamma:" HorizontalAlignment="Left" Margin="-5,6,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="69,8,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding SDRTransferFunctions}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SDRTransferFunction}"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="405,10,0,0" TextWrapping="Wrap" Text="{Binding CustomGamma, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbCustomGamma" Height="18" InputScope="Number"/>
<Label Content="SDR minimum brightness (nits):" HorizontalAlignment="Left" Margin="-5,37,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="69,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMinBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness" Height="18" InputScope="Number"/>
<Label Content="SDR maximum brightness (nits):" HorizontalAlignment="Left" Margin="197,37,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="405,41,0,0" TextWrapping="Wrap" Text="{Binding SDRMaxBrightness, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMaxBrightness" Height="18" InputScope="Number"/>
<Label Content="SDR brightness boost (%):" HorizontalAlignment="Left" Margin="-5,64,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="69,68,0,0" TextWrapping="Wrap" Text="{Binding SDRBrightnessBoost, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbSDRMinBrightness_Copy6" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced Profile Settings" Padding="6" Height="78" Width="734" HorizontalAlignment="Left">
<Grid Height="47">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="4*"/>
</Grid.ColumnDefinitions>
<Label Content="MHC2 MinCLL (nits):" HorizontalAlignment="Left" Margin="-3,6,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="66,10,0,0" TextWrapping="Wrap" Text="{Binding MinCLL, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance_Copy" Height="18" InputScope="Number"/>
<Label Content="MHC2 MaxCLL (nits):" HorizontalAlignment="Left" Margin="201,6,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.Column="1"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="400,10,0,0" TextWrapping="Wrap" Text="{Binding MaxCLL, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="120" x:Name="tbMinLuminance_Copy1" Height="18" InputScope="Number"/>
</Grid>
</GroupBox>
<GroupBox DockPanel.Dock="Right" Margin="0,0,0,0" Header="Generate Options" Padding="6" Height="197" Width="733" HorizontalAlignment="Left">
<Grid Height="163" Margin="-2,0,2,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="27*"/>
</Grid.ColumnDefinitions>
<Button IsEnabled="{Binding IsValid}" Click="Button_Click" Content="Generate" Margin="46,132,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Height="20" Width="75" Grid.Column="1"/>
<Label Content="Overwrite existing profile:" HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="46,42,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" SelectedValue="{Binding SelectedExistingProfile}" ItemsSource="{Binding ExistingProfiles}"/>
<Label Content="New profile name:" HorizontalAlignment="Left" Margin="0,73,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="46,77,0,0" TextWrapping="Wrap" Text="{Binding NewProfileName}" VerticalAlignment="Top" Width="318" x:Name="tbName" Height="18"/>
<Label Content="Save option:" HorizontalAlignment="Left" Margin="0,8,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<ComboBox Margin="46,10,0,0" HorizontalAlignment="Left" Width="318" VerticalAlignment="Top" Grid.Column="1" Height="22" ItemsSource="{Binding SaveOptions}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding SaveOption}" />
<Label Content="Description:" HorizontalAlignment="Left" Margin="0,101,0,0" VerticalAlignment="Top" Width="208" Height="26" Grid.ColumnSpan="2"/>
<TextBox Grid.Column="1" HorizontalAlignment="Left" Margin="46,105,0,0" TextWrapping="Wrap" Text="{Binding Description, NotifyOnValidationError=True, ValidatesOnDataErrors=True}" VerticalAlignment="Top" Width="318" x:Name="tbName_Copy" Height="18"/>
</Grid>
</GroupBox>
<StackPanel HorizontalAlignment="Right" Margin="0,6,0,0" VerticalAlignment="Bottom"/>
<Button Click="Close_Click" IsCancel="True" Content="Close" Width="75" HorizontalAlignment="Right" VerticalAlignment="Bottom"/>
</StackPanel>
</src:BaseWindow>
@@ -0,0 +1,152 @@
using ColorControl.Shared.Forms;
using ColorControl.Shared.Native;
using ColorControl.Shared.Services;
using ColorControl.Shared.XForms;
using MHC2Gen;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms.Integration;
using System.Windows.Navigation;
namespace ColorControl.XForms
{
public partial class ColorProfileWindow : BaseWindow
{
private ColorProfileViewModel _viewModel;
private readonly WinApiAdminService _winApiAdminService;
public ColorProfileWindow(WinApiAdminService winApiAdminService)
{
_viewModel = new ColorProfileViewModel();
DataContext = _viewModel;
InitializeComponent();
_winApiAdminService = winApiAdminService;
}
private void OnRequestNavigate(object sender, RequestNavigateEventArgs e)
{
var processStartInfo = new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri)
{
UseShellExecute = true,
};
System.Diagnostics.Process.Start(processStartInfo);
}
public static void CreateAndShow(bool show = true)
{
if (Application.Current == null)
{
new Application();
}
var window = Program.ServiceProvider.GetRequiredService<ColorProfileWindow>();
if (show)
{
ElementHost.EnableModelessKeyboardInterop(window);
window.WindowState = WindowState.Normal;
window.Show();
window.Topmost = true;
window.Topmost = false;
}
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
}
protected override void OnClosing(CancelEventArgs e)
{
//e.Cancel = true;
//Hide();
}
protected override void OnStateChanged(EventArgs e)
{
if (WindowState == WindowState.Minimized)
{
Close();
}
base.OnStateChanged(e);
}
private void Close_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (!_viewModel.Displays.Any())
{
MessageForms.WarningOk("There are no displays available that support HDR and have it enabled. Please activate HDR first.");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var command = new GenerateProfileCommand
{
Description = _viewModel.Description,
MinCLL = _viewModel.MinCLL,
MaxCLL = _viewModel.MaxCLL,
BlackLuminance = _viewModel.BlackLuminance,
WhiteLuminance = _viewModel.WhiteLuminance,
SDRMinBrightness = _viewModel.SDRMinBrightness,
SDRMaxBrightness = _viewModel.SDRMaxBrightness,
SDRTransferFunction = _viewModel.SDRTransferFunction,
SDRBrightnessBoost = _viewModel.SDRBrightnessBoost,
ColorGamut = _viewModel.ColorGamut,
Gamma = _viewModel.CustomGamma,
DevicePrimaries = _viewModel.GetDevicePrimaries(),
};
var bytes = MHC2Wrapper.GenerateSdrAcmProfile(command);
if (_viewModel.SaveOption == SaveOption.SaveToFile)
{
var saveFileDialog = new System.Windows.Forms.SaveFileDialog
{
FileName = _viewModel.NewProfileName
};
if (saveFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
File.WriteAllBytes(saveFileDialog.FileName, bytes);
}
return;
}
var tempFilename = Path.GetFullPath(_viewModel.ProfileName, Path.GetTempPath());
if (File.Exists(tempFilename))
{
File.Delete(tempFilename);
}
File.WriteAllBytes(tempFilename, bytes);
_winApiAdminService.UninstallColorProfile(tempFilename);
CCD.InstallColorProfile(tempFilename);
if (_viewModel.SaveOption != SaveOption.InstallAndSetAsDefault)
{
return;
}
var displayName = _viewModel.SelectedDisplayName;
var profileName = Path.GetFileName(tempFilename);
CCD.SetDisplayDefaultColorProfile(displayName, profileName);
}
}
}
+64
View File
@@ -0,0 +1,64 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms
{
public class CmsContext
{
public IntPtr Handle { get; }
public bool OwnedByGC { get; private set; }
static readonly ConcurrentDictionary<IntPtr, CmsContext> GCOwnedContext = new();
public static CmsContext Default { get; } = new CmsContext(IntPtr.Zero, false);
private void MoveToGC()
{
OwnedByGC = true;
GCOwnedContext.TryAdd(Handle, this);
}
public CmsContext() {
Handle = CmsNative.cmsCreateContext(IntPtr.Zero, IntPtr.Zero);
MoveToGC();
}
private CmsContext(IntPtr handle, bool moveOwnership){
Handle = handle;
if (moveOwnership)
{
MoveToGC();
}
}
public static CmsContext GetFromHandle(IntPtr handle, bool moveOwnership = false)
{
if (handle == IntPtr.Zero) return Default;
if (GCOwnedContext.TryGetValue(handle, out var x)) return x;
return new CmsContext(handle, moveOwnership);
}
public double SetAdaptionState(double state)
{
return CmsNative.cmsSetAdaptationStateTHR(Handle, state);
}
~CmsContext()
{
if (OwnedByGC && Handle != IntPtr.Zero )
{
if (GCOwnedContext.TryRemove(new(Handle, this))) {
CmsNative.cmsDeleteContext(Handle);
}
}
}
}
}
+539
View File
@@ -0,0 +1,539 @@
using System;
#pragma warning disable CS0649
#pragma warning disable CS0169
namespace LittleCms
{
public enum TagSignature
{
AToB0Tag = 0x41324230, // 'A2B0'
AToB1Tag = 0x41324231, // 'A2B1'
AToB2Tag = 0x41324232, // 'A2B2'
BlueColorantTag = 0x6258595A, // 'bXYZ'
BlueMatrixColumnTag = 0x6258595A, // 'bXYZ'
BlueTRCTag = 0x62545243, // 'bTRC'
BToA0Tag = 0x42324130, // 'B2A0'
BToA1Tag = 0x42324131, // 'B2A1'
BToA2Tag = 0x42324132, // 'B2A2'
CalibrationDateTimeTag = 0x63616C74, // 'calt'
CharTargetTag = 0x74617267, // 'targ'
ChromaticAdaptationTag = 0x63686164, // 'chad'
ChromaticityTag = 0x6368726D, // 'chrm'
ColorantOrderTag = 0x636C726F, // 'clro'
ColorantTableTag = 0x636C7274, // 'clrt'
ColorantTableOutTag = 0x636C6F74, // 'clot'
ColorimetricIntentImageStateTag = 0x63696973, // 'ciis'
CopyrightTag = 0x63707274, // 'cprt'
CrdInfoTag = 0x63726469, // 'crdi'
DataTag = 0x64617461, // 'data'
DateTimeTag = 0x6474696D, // 'dtim'
DeviceMfgDescTag = 0x646D6E64, // 'dmnd'
DeviceModelDescTag = 0x646D6464, // 'dmdd'
DeviceSettingsTag = 0x64657673, // 'devs'
DToB0Tag = 0x44324230, // 'D2B0'
DToB1Tag = 0x44324231, // 'D2B1'
DToB2Tag = 0x44324232, // 'D2B2'
DToB3Tag = 0x44324233, // 'D2B3'
BToD0Tag = 0x42324430, // 'B2D0'
BToD1Tag = 0x42324431, // 'B2D1'
BToD2Tag = 0x42324432, // 'B2D2'
BToD3Tag = 0x42324433, // 'B2D3'
GamutTag = 0x67616D74, // 'gamt'
GrayTRCTag = 0x6b545243, // 'kTRC'
GreenColorantTag = 0x6758595A, // 'gXYZ'
GreenMatrixColumnTag = 0x6758595A, // 'gXYZ'
GreenTRCTag = 0x67545243, // 'gTRC'
LuminanceTag = 0x6C756d69, // 'lumi'
MeasurementTag = 0x6D656173, // 'meas'
MediaBlackPointTag = 0x626B7074, // 'bkpt'
MediaWhitePointTag = 0x77747074, // 'wtpt'
MHC2 = 0x4D484332, // 'MHC2'
NamedColorTag = 0x6E636f6C, // 'ncol' // Deprecated by the ICC
NamedColor2Tag = 0x6E636C32, // 'ncl2'
OutputResponseTag = 0x72657370, // 'resp'
PerceptualRenderingIntentGamutTag = 0x72696730, // 'rig0'
Preview0Tag = 0x70726530, // 'pre0'
Preview1Tag = 0x70726531, // 'pre1'
Preview2Tag = 0x70726532, // 'pre2'
ProfileDescriptionTag = 0x64657363, // 'desc'
ProfileDescriptionMLTag = 0x6473636d, // 'dscm'
ProfileSequenceDescTag = 0x70736571, // 'pseq'
ProfileSequenceIdTag = 0x70736964, // 'psid'
Ps2CRD0Tag = 0x70736430, // 'psd0'
Ps2CRD1Tag = 0x70736431, // 'psd1'
Ps2CRD2Tag = 0x70736432, // 'psd2'
Ps2CRD3Tag = 0x70736433, // 'psd3'
Ps2CSATag = 0x70733273, // 'ps2s'
Ps2RenderingIntentTag = 0x70733269, // 'ps2i'
RedColorantTag = 0x7258595A, // 'rXYZ'
RedMatrixColumnTag = 0x7258595A, // 'rXYZ'
RedTRCTag = 0x72545243, // 'rTRC'
SaturationRenderingIntentGamutTag = 0x72696732, // 'rig2'
ScreeningDescTag = 0x73637264, // 'scrd'
ScreeningTag = 0x7363726E, // 'scrn'
TechnologyTag = 0x74656368, // 'tech'
UcrBgTag = 0x62666420, // 'bfd '
ViewingCondDescTag = 0x76756564, // 'vued'
ViewingConditionsTag = 0x76696577, // 'view'
VcgtTag = 0x76636774, // 'vcgt'
MetaTag = 0x6D657461, // 'meta'
ArgyllArtsTag = 0x61727473 // 'arts'
}
/// <summary>
/// ICC Color spaces
/// </summary>
public enum ColorSpaceSignature
{
XYZ = 0x58595A20, // 'XYZ '
Lab = 0x4C616220, // 'Lab '
Luv = 0x4C757620, // 'Luv '
YCbCr = 0x59436272, // 'YCbr'
Yxy = 0x59787920, // 'Yxy '
Rgb = 0x52474220, // 'RGB '
Gray = 0x47524159, // 'GRAY'
Hsv = 0x48535620, // 'HSV '
Hls = 0x484C5320, // 'HLS '
Cmyk = 0x434D594B, // 'CMYK'
Cmy = 0x434D5920, // 'CMY '
MCH1 = 0x4D434831, // 'MCH1'
MCH2 = 0x4D434832, // 'MCH2'
MCH3 = 0x4D434833, // 'MCH3'
MCH4 = 0x4D434834, // 'MCH4'
MCH5 = 0x4D434835, // 'MCH5'
MCH6 = 0x4D434836, // 'MCH6'
MCH7 = 0x4D434837, // 'MCH7'
MCH8 = 0x4D434838, // 'MCH8'
MCH9 = 0x4D434839, // 'MCH9'
MCHA = 0x4D434841, // 'MCHA'
MCHB = 0x4D434842, // 'MCHB'
MCHC = 0x4D434843, // 'MCHC'
MCHD = 0x4D434844, // 'MCHD'
MCHE = 0x4D434845, // 'MCHE'
MCHF = 0x4D434846, // 'MCHF'
Named = 0x6e6d636c, // 'nmcl'
MultiColor1 = 0x31434C52, // '1CLR'
MultiColor2 = 0x32434C52, // '2CLR'
MultiColor3 = 0x33434C52, // '3CLR'
MultiColor4 = 0x34434C52, // '4CLR'
MultiColor5 = 0x35434C52, // '5CLR'
MultiColor6 = 0x36434C52, // '6CLR'
MultiColor7 = 0x37434C52, // '7CLR'
MultiColor8 = 0x38434C52, // '8CLR'
MultiColor9 = 0x39434C52, // '9CLR'
MultiColor10 = 0x41434C52, // 'ACLR'
MultiColor11 = 0x42434C52, // 'BCLR'
MultiColor12 = 0x43434C52, // 'CCLR'
MultiColor13 = 0x44434C52, // 'DCLR'
MultiColor14 = 0x45434C52, // 'ECLR'
MultiColor15 = 0x46434C52, // 'FCLR'
LuvK = 0x4C75764B // 'LuvK'
}
/// <summary>
/// ICC Profile Class
/// </summary>
public enum ProfileClassSignature
{
Input = 0x73636E72, // 'scnr'
Display = 0x6D6E7472, // 'mntr'
Output = 0x70727472, // 'prtr'
Link = 0x6C696E6B, // 'link'
Abstract = 0x61627374, // 'abst'
ColorSpace = 0x73706163, // 'spac'
NamedColor = 0x6e6d636c // 'nmcl'
}
public struct CIEXYZ
{
public double X;
public double Y;
public double Z;
public static CIEXYZ operator +(in CIEXYZ a, in CIEXYZ b)
{
return new()
{
X = a.X + b.X,
Y = a.Y + b.Y,
Z = a.Z + b.Z
};
}
public static CIEXYZ operator -(in CIEXYZ a, in CIEXYZ b)
{
return new()
{
X = a.X - b.X,
Y = a.Y - b.Y,
Z = a.Z - b.Z
};
}
public static CIEXYZ operator -(in CIEXYZ a)
{
return new()
{
X = -a.X,
Y = -a.Y,
Z = -a.Z
};
}
}
public struct CIExyY
{
public double x;
public double y;
public double Y;
}
public enum InfoType
{
Description = 0,
Manufacturer = 1,
Model = 2,
Copyright = 3
}
public enum ProfileUsedDirection
{
AsInput = 0,
AsOutput = 1,
AsProof = 2,
}
public struct CIEXYZTRIPLE
{
public CIEXYZ Red;
public CIEXYZ Green;
public CIEXYZ Blue;
}
public struct CIExyYTRIPLE
{
public CIExyY Red;
public CIExyY Green;
public CIExyY Blue;
}
public struct ICCData
{
public uint Flag;
public Memory<byte> Data;
}
internal struct _ICCDataHeader
{
public uint len;
public uint flag;
}
internal unsafe struct _ICCData
{
public uint len;
public uint flag;
public fixed byte data[1];
internal static unsafe ICCData ToManaged(_ICCData* ptr)
{
var result = new ICCData();
result.Flag = ptr->flag;
result.Data = new ReadOnlySpan<byte>(ptr->data, (int)ptr->len).ToArray();
return result;
}
}
public struct CIELab
{
public double L;
public double a;
public double b;
}
public struct CIELCh
{
public double L;
public double C;
public double h;
}
public struct JCh
{
public double J;
public double C;
public double h;
}
public enum IlluminantType
{
UNKNOWN = 0x0000000,
D50 = 0x0000001,
D65 = 0x0000002,
D93 = 0x0000003,
F2 = 0x0000004,
D55 = 0x0000005,
A = 0x0000006,
E = 0x0000007,
F8 = 0x0000008,
}
public struct ICCMeasurementConditions
{
public uint Observer; // 0 = unknown, 1=CIE 1931, 2=CIE 1964
public CIEXYZ Backing; // Value of backing
public uint Geometry; // 0=unknown, 1=45/0, 0/45 2=0d, d/0
public double Flare; // 0..1.0
public IlluminantType IlluminantType;
}
public struct ICCViewingConditions
{
public CIEXYZ IlluminantXYZ; // Not the same struct as CAM02,
public CIEXYZ SurroundXYZ; // This is for storing the tag
public IlluminantType IlluminantType; // viewing condition
}
public enum HeaderFlags
{
EmbeddedProfile = 1,
UseWithEmbeddedDataOnly = 2
}
internal struct CmsStructTm
{
public int tm_sec; // seconds after the minute - [0, 60] including leap second
public int tm_min; // minutes after the hour - [0, 59]
public int tm_hour; // hours since midnight - [0, 23]
public int tm_mday; // day of the month - [1, 31]
public int tm_mon; // months since January - [0, 11]
public int tm_year; // years since 1900
public int tm_wday; // days since Sunday - [0, 6]
public int tm_yday; // days since January 1 - [0, 365]
public int tm_isdst; // daylight savings time flag
public DateTime ToDateTime() => new DateTime(tm_year, tm_mon + 1, tm_mday, tm_hour, tm_min, tm_sec);
public static CmsStructTm FromDateTime(DateTime d)
{
return new CmsStructTm
{
tm_year = d.Year,
tm_mon = d.Month - 1,
tm_mday = d.Day,
tm_hour = d.Hour,
tm_min = d.Minute,
tm_sec = d.Second,
tm_wday = (int)d.DayOfWeek,
tm_yday = d.DayOfYear,
tm_isdst = d.IsDaylightSavingTime() ? 1 : 0
};
}
}
public enum SpotShape
{
Unknown = 0,
PrinterDefault = 1,
Round = 2,
Diamond = 3,
Ellipse = 4,
Line = 5,
Square = 6,
Cross = 7,
}
public struct cmsScreeningChannel
{
public double Frequency;
public double ScreenAngle;
public SpotShape SpotShape;
}
/// <summary>
/// Where to place/locate the stages in the pipeline chain
/// </summary>
public enum StageLoc
{
AtBegin,
AtEnd,
}
/// <summary>
/// Multi process elements types
/// </summary>
public enum StageSignature
{
CurveSetElemType = 0x63767374, //'cvst'
MatrixElemType = 0x6D617466, //'matf'
CLutElemType = 0x636C7574, //'clut'
BAcsElemType = 0x62414353, // 'bACS'
EAcsElemType = 0x65414353, // 'eACS'
// Custom from here, not in the ICC Spec
XYZ2LabElemType = 0x6C327820, // 'l2x '
Lab2XYZElemType = 0x78326C20, // 'x2l '
NamedColorElemType = 0x6E636C20, // 'ncl '
LabV2toV4 = 0x32203420, // '2 4 '
LabV4toV2 = 0x34203220, // '4 2 '
// Identities
IdentityElemType = 0x69646E20, // 'idn '
// Float to floatPCS
Lab2FloatPCS = 0x64326C20, // 'd2l '
FloatPCS2Lab = 0x6C326420, // 'l2d '
XYZ2FloatPCS = 0x64327820, // 'd2x '
FloatPCS2XYZ = 0x78326420, // 'x2d '
ClipNegativesElemType = 0x636c7020 // 'clp '
}
public struct cmsViewingConditions
{
public const int AVG_SURROUND = 1;
public const int DIM_SURROUND = 2;
public const int DARK_SURROUND = 3;
public const int CUTSHEET_SURROUND = 4;
public const double D_CALCULATE = (-1);
public CIEXYZ whitePoint;
public double Yb;
public double La;
public uint surround;
public double D_value;
}
public enum CmsError
{
UNDEFINED = 0,
FILE = 1,
RANGE = 2,
INTERNAL = 3,
NULL = 4,
READ = 5,
SEEK = 6,
WRITE = 7,
UNKNOWN_EXTENSION = 8,
COLORSPACE_CHECK = 9,
ALREADY_DEFINED = 10,
BAD_SIGNATURE = 11,
CORRUPTION_DETECTED = 12,
NOT_SUITABLE = 13,
}
public enum RenderingIntent
{
// ICC Intents
PERCEPTUAL = 0,
RELATIVE_COLORIMETRIC = 1,
SATURATION = 2,
ABSOLUTE_COLORIMETRIC = 3,
// Non-ICC intents
PRESERVE_K_ONLY_PERCEPTUAL = 10,
PRESERVE_K_ONLY_RELATIVE_COLORIMETRIC = 11,
PRESERVE_K_ONLY_SATURATION = 12,
PRESERVE_K_PLANE_PERCEPTUAL = 13,
PRESERVE_K_PLANE_RELATIVE_COLORIMETRIC = 14,
PRESERVE_K_PLANE_SATURATION = 15,
}
public enum TransformFlags
{
NOCACHE = 0x0040, // Inhibit 1-pixel cache
NOOPTIMIZE = 0x0100, // Inhibit optimizations
NULLTRANSFORM = 0x0200, // Don't transform anyway
// Proofing flags
GAMUTCHECK = 0x1000, // Out of Gamut alarm
SOFTPROOFING = 0x4000, // Do softproofing
// Misc
BLACKPOINTCOMPENSATION = 0x2000,
NOWHITEONWHITEFIXUP = 0x0004, // Don't fix scum dot
HIGHRESPRECALC = 0x0400, // Use more memory to give better accuracy
LOWRESPRECALC = 0x0800, // Use less memory to minimize resources
// For devicelink creation
DEVICELINK_8BITS = 0x0008, // Create 8 bits devicelinks
GUESSDEVICECLASS = 0x0020, // Guess device class (for transform2devicelink)
KEEP_SEQUENCE = 0x0080, // Keep profile sequence for devicelink creation
// Specific to a particular optimizations
FORCE_CLUT = 0x0002, // Force CLUT optimization
CLUT_POST_LINEARIZATION = 0x0001, // create postlinearization tables if possible
CLUT_PRE_LINEARIZATION = 0x0010, // create prelinearization tables if possible
// Specific to unbounded mode
NONEGATIVES = 0x8000, // Prevent negative numbers in floating point transforms
// Copy alpha channels when transforming
COPY_ALPHA = 0x04000000,
// Fine-tune control over number of gridpoints
// #define cmsFLAGS_GRIDPOINTS(n) (((n) & 0xFF) << 16)
// CRD special
NODEFAULTRESOURCEDEF = 0x01000000
}
public enum PSResourceType
{
CSA,
CRD
}
public enum PixelType
{
/// <summary>
/// Don't check colorspace
/// </summary>
ANY = 0,
// 1 & 2 are reserved
GRAY = 3,
RGB = 4,
CMY = 5,
CMYK = 6,
YCbCr = 7,
/// <summary>
/// Lu'v'
/// </summary>
YUV = 8,
XYZ = 9,
Lab = 10,
/// <summary>
/// Lu'v'K
/// </summary>
YUVK = 11,
HSV = 12,
HLS = 13,
Yxy = 14,
MCH1 = 15,
MCH2 = 16,
MCH3 = 17,
MCH4 = 18,
MCH5 = 19,
MCH6 = 20,
MCH7 = 21,
MCH8 = 22,
MCH9 = 23,
MCH10 = 24,
MCH11 = 25,
MCH12 = 26,
MCH13 = 27,
MCH14 = 28,
MCH15 = 29,
LabV2 = 30,
}
}
+84
View File
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace LittleCms
{
public class CmsException : Exception
{
public CmsError CmsError { get; }
public CmsException() : base("Error occurred in lcms2") { CmsError = CmsError.UNDEFINED; }
public CmsException(CmsError code, string message) : base(message) { CmsError = code; }
}
partial class CmsNative
{
private static readonly ThreadLocal<CmsException?> LastError;
static CmsNative()
{
var libver = cmsGetEncodedCMMversion();
if (libver != LCMS_VERSION)
{
throw new TypeLoadException("lcms2 version mismatch");
}
LastError = new();
cmsSetLogErrorHandler(ErrorCallback);
}
private static void ErrorCallback(IntPtr ContextID, CmsError ErrorCode, string Text)
{
LastError.Value = new(ErrorCode, Text);
}
private static void ThrowLastTlsError()
{
if (LastError.IsValueCreated && LastError.Value != null)
{
var ex = LastError.Value;
LastError.Value = null;
throw ex;
}
else
{
throw new CmsException();
}
}
public static uint CheckError(uint value)
{
if (value == 0)
{
ThrowLastTlsError();
}
return value;
}
public static void CheckError(bool value)
{
if (!value)
{
ThrowLastTlsError();
}
}
public static IntPtr CheckError(IntPtr value)
{
if (value == IntPtr.Zero)
{
ThrowLastTlsError();
}
return value;
}
public unsafe static void* CheckError(void* value)
{
if (value == null)
{
ThrowLastTlsError();
}
return value;
}
}
}
+34
View File
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms
{
public static class CmsExtension
{
public static CIExyY ToCIExyY(this CIEXYZ xyz)
{
return new CIExyY { x = xyz.X / (xyz.X + xyz.Y + xyz.Z), y = xyz.Y / (xyz.X + xyz.Y + xyz.Z), Y = xyz.Y };
}
public static CIExyY ToXY(this CIEXYZ xyz)
{
var sum = xyz.X + xyz.Y + xyz.Z;
return new() { x = xyz.X / sum, y = xyz.Y / sum, Y = 1 };
}
public static CIEXYZ ToXYZ(this CIExyY xyY)
{
return new() { X = xyY.x * xyY.Y / xyY.y, Y = xyY.Y, Z = (1 - xyY.x - xyY.y) * xyY.Y / xyY.y };
}
public static int GetChannelCount(this ColorSpaceSignature sig)
{
return (int)CmsNative.cmsChannelsOf(sig);
}
}
}
+20
View File
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace LittleCms
{
public class CmsGlobal
{
public static CIEXYZ D50XYZ { get; } = CmsNative.cmsD50_XYZ();
public static CIExyY D50xyY { get; } = CmsNative.cmsD50_xyY();
public unsafe static CIEXYZ AdaptToIlluminant(in CIEXYZ SourceWhitePt, in CIEXYZ Illuminant, in CIEXYZ Value)
{
CmsNative.CheckError(CmsNative.cmsAdaptToIlluminant(out var result, SourceWhitePt, Illuminant, Value));
return result;
}
}
}
+958
View File
@@ -0,0 +1,958 @@
using LittleCms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
#pragma warning disable CS0649
#pragma warning disable CS0169
namespace LittleCms
{
internal unsafe partial class CmsNative
{
private const int LCMS_VERSION = 2131;
private const string LIB = "lcms2";
public static readonly Encoding WcharEncoding = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Encoding.Unicode : Encoding.UTF32;
// Get LittleCMS version (for shared objects) -----------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsGetEncodedCMMversion();
// Context handling --------------------------------------------------------------------------------------------------------
// Each context holds its owns globals and its own plug-ins. There is a global context with the id = 0 for lecacy compatibility
// though using the global context is not recommended. Proper context handling makes lcms more thread-safe.
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateContext(IntPtr Plugin, IntPtr UserData);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDeleteContext(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsDupContext(IntPtr ContextID, IntPtr NewUserData);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetContextUserData(IntPtr ContextID);
// Plug-In registering --------------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPlugin(void* Plugin);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPluginTHR(IntPtr ContextID, void* Plugin);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsUnregisterPlugins();
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsUnregisterPluginsTHR(IntPtr ContextID);
// Error logging ----------------------------------------------------------------------------------------------------------
// There is no error handling at all. When a function fails, it returns proper value.
// For example, all create functions does return NULL on failure. Other may return FALSE.
// It may be interesting, for the developer, to know why the function is failing.
// for that reason, lcms2 does offer a logging function. This function will get
// an ENGLISH string with some clues on what is going wrong. You can show this
// info to the end user if you wish, or just create some sort of log on disk.
// The logging function should NOT terminate the program, as this obviously can leave
// unfreed resources. It is the programmer's responsibility to check each function
// return code to make sure it didn't fail.
// Error logger is called with the ContextID when a message is raised. This gives the
// chance to know which thread is responsible of the warning and any environment associated
// with it. Non-multithreading applications may safely ignore this parameter.
// Note that under certain special circumstances, ContextID may be NULL.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void cmsLogErrorHandlerFunction(IntPtr ContextID, CmsError ErrorCode, [In, MarshalAs(UnmanagedType.LPStr)] string Text);
// Allows user to set any specific logger
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetLogErrorHandler(cmsLogErrorHandlerFunction Fn);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetLogErrorHandlerTHR(IntPtr ContextID, cmsLogErrorHandlerFunction Fn);
// Returns pointers to constant structs
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ref readonly CIEXYZ cmsD50_XYZ();
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ref readonly CIExyY cmsD50_xyY();
// Colorimetric space conversions
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsXYZ2xyY(out CIExyY Dest, in CIEXYZ Source);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsxyY2XYZ(CIEXYZ* Dest, in CIExyY Source);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsXYZ2Lab(in CIEXYZ WhitePoint, out CIELab Lab, in CIEXYZ xyz);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsLab2XYZ(in CIEXYZ WhitePoint, out CIEXYZ xyz, in CIELab Lab);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsLab2LCh(out CIELCh LCh, in CIELab Lab);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsLCh2Lab(out CIELab Lab, in CIELCh LCh);
// Encoding /Decoding on PCS
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsLabEncoded2Float(out CIELab Lab, in ushort wLab_3);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsLabEncoded2FloatV2(out CIELab Lab, in ushort wLab_3);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsFloat2LabEncoded(out ushort wLab_3, in CIELab Lab);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsFloat2LabEncodedV2(out ushort wLab_3, in CIELab Lab);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsXYZEncoded2Float(out CIEXYZ fxyz, in ushort XYZ_3);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsFloat2XYZEncoded(out ushort XYZ_3, in CIEXYZ fXYZ);
// DeltaE metrics
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsDeltaE(in CIELab Lab1, in CIELab Lab2);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsCIE94DeltaE(in CIELab Lab1, in CIELab Lab2);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsBFDdeltaE(in CIELab Lab1, in CIELab Lab2);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsCMCdeltaE(in CIELab Lab1, in CIELab Lab2, double l, double c);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsCIE2000DeltaE(in CIELab Lab1, in CIELab Lab2, double Kl, double Kc, double Kh);
// Temperature <-> Chromaticity (Black body)
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsWhitePointFromTemp(out CIExyY WhitePoint, double TempK);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsTempFromWhitePoint(double* TempK, in CIExyY WhitePoint);
// Chromatic adaptation
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsAdaptToIlluminant(out CIEXYZ Result, in CIEXYZ SourceWhitePt,
in CIEXYZ Illuminant,
in CIEXYZ Value);
// CIECAM02 ---------------------------------------------------------------------------------------------------
// Viewing conditions. Please note those are CAM model viewing conditions, and not the ICC tag viewing
// conditions, which I'm naming cmsICCViewingConditions to make differences evident. Unfortunately, the tag
// cannot deal with surround La, Yb and D value so is basically useless to store CAM02 viewing conditions.
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCIECAM02Init(IntPtr ContextID, in cmsViewingConditions pVC);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsCIECAM02Done(IntPtr hModel);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsCIECAM02Forward(IntPtr hModel, in CIEXYZ pIn, out JCh pOut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsCIECAM02Reverse(IntPtr hModel, in JCh pIn, out CIEXYZ pOut);
// Tone curves -----------------------------------------------------------------------------------------
// This describes a curve segment. For a table of supported types, see the manual. User can increase the number of
// available types by using a proper plug-in. Parametric segments allow 10 parameters at most
public struct cmsCurveSegment
{
public float x0, x1; // Domain; for x0 < x <= x1
public int Type; // Parametric type, Type == 0 means sampled segment. Negative values are reserved
public fixed double Params[10]; // Parameters if Type != 0
public uint nGridPoints; // Number of grid points if Type == 0
public float* SampledPoints; // Points to an array of floats if Type == 0
}
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsBuildSegmentedToneCurve(IntPtr ContextID, uint nSegments, in cmsCurveSegment Segments);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsBuildParametricToneCurve(IntPtr ContextID, int Type, in double Params);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsBuildGamma(IntPtr ContextID, double Gamma);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsBuildTabulatedToneCurve16(IntPtr ContextID, uint nEntries, in ushort values);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsBuildTabulatedToneCurveFloat(IntPtr ContextID, uint nEntries, in float values);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsFreeToneCurve(IntPtr Curve);
//[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
//public static extern void cmsFreeToneCurveTriple(IntPtr Curve[3]);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsDupToneCurve(IntPtr Src);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsReverseToneCurve(IntPtr InGamma);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsReverseToneCurveEx(uint nResultSamples, IntPtr InGamma);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsJoinToneCurve(IntPtr ContextID, IntPtr X, IntPtr Y, uint nPoints);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsSmoothToneCurve(IntPtr Tab, double lambda);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern float cmsEvalToneCurveFloat(IntPtr Curve, float v);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ushort cmsEvalToneCurve16(IntPtr Curve, ushort v);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsToneCurveMultisegment(IntPtr InGamma);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsToneCurveLinear(IntPtr Curve);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsToneCurveMonotonic(IntPtr t);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsToneCurveDescending(IntPtr t);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsGetToneCurveParametricType(IntPtr t);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsEstimateGamma(IntPtr t, double Precision);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ref readonly double cmsGetToneCurveParams(IntPtr t);
// Tone curve tabular estimation
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetToneCurveEstimatedTableEntries(IntPtr t);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ref readonly ushort cmsGetToneCurveEstimatedTable(IntPtr t);
// Implements pipelines of multi-processing elements -------------------------------------------------------------
// Those are hi-level pipelines
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsPipelineAlloc(IntPtr ContextID, uint InputChannels, uint OutputChannels);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsPipelineFree(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsPipelineDup(IntPtr Orig);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetPipelineContextID(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsPipelineInputChannels(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsPipelineOutputChannels(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsPipelineStageCount(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsPipelineGetPtrToFirstStage(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsPipelineGetPtrToLastStage(IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsPipelineEval16(in ushort In, out ushort Out, IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsPipelineEvalFloat(in float In, out float Out, IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPipelineEvalReverseFloat(ref float Target, ref float Result, ref float Hint, IntPtr lut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPipelineCat(IntPtr l1, IntPtr l2);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPipelineSetSaveAs8bitsFlag(IntPtr lut, bool On);
}
internal unsafe partial class CmsNative
{
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsPipelineInsertStage(IntPtr lut, StageLoc loc, IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsPipelineUnlinkStage(IntPtr lut, StageLoc loc, out IntPtr mpe);
// This function is quite useful to analyze the structure of a Pipeline and retrieve the Stage elements
// that conform the Pipeline. It should be called with the Pipeline, the number of expected elements and
// then a list of expected types followed with a list of double pointers to Stage elements. If
// the function founds a match with current pipeline, it fills the pointers and returns TRUE
// if not, returns FALSE without touching anything.
[DllImport(LIB, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
public static extern bool cmsPipelineCheckAndRetreiveStages(IntPtr Lut, uint n, __arglist);
// Matrix has double precision and CLUT has only float precision. That is because an ICC profile can encode
// matrices with far more precision that CLUTS
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocIdentity(IntPtr ContextID, uint nChannels);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocToneCurves(IntPtr ContextID, uint nChannels, in IntPtr Curves);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocMatrix(IntPtr ContextID, uint Rows, uint Cols, in double Matrix, in double Offset);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocCLut16bit(IntPtr ContextID, uint nGridPoints, uint inputChan, uint outputChan, in ushort Table);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocCLutFloat(IntPtr ContextID, uint nGridPoints, uint inputChan, uint outputChan, in float Table);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocCLut16bitGranular(IntPtr ContextID, in uint clutPoints, uint inputChan, uint outputChan, in ushort Table);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageAllocCLutFloatGranular(IntPtr ContextID, in uint clutPoints, uint inputChan, uint outputChan, in float Table);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageDup(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsStageFree(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageNext(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsStageInputChannels(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsStageOutputChannels(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern StageSignature cmsStageType(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsStageData(IntPtr mpe);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetStageContextID(IntPtr mpe);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int cmsSAMPLER16(ushort* In, ushort* Out, IntPtr Cargo);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int cmsSAMPLERFLOAT(float* In, float* Out, IntPtr Cargo);
const uint SAMPLER_INSPECT = 0x01000000;
// For CLUT only
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsStageSampleCLut16bit(IntPtr mpe, cmsSAMPLER16 Sampler, void* Cargo, uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsStageSampleCLutFloat(IntPtr mpe, cmsSAMPLERFLOAT Sampler, void* Cargo, uint dwFlags);
// Slicers
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsSliceSpace16(uint nInputs, in uint clutPoints,
cmsSAMPLER16 Sampler, void* Cargo);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsSliceSpaceFloat(uint nInputs, in uint clutPoints,
cmsSAMPLERFLOAT Sampler, void* Cargo);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsMLUalloc(IntPtr ContextID, uint nItems);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsMLUfree(IntPtr mlu);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsMLUdup(IntPtr mlu);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, CharSet = CharSet.Ansi)]
public static extern bool cmsMLUsetASCII(IntPtr mlu,
in uint LanguageCode, in uint CountryCode,
string ASCIIString);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsMLUsetWide(IntPtr mlu,
in uint LanguageCode, in uint CountryCode,
in byte WideString);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsMLUgetASCII(IntPtr mlu,
in uint LanguageCode, in uint CountryCode,
byte* Buffer, uint BufferSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsMLUgetWide(IntPtr mlu,
in uint LanguageCode, in uint CountryCode,
byte* Buffer, uint BufferSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsMLUgetTranslation(IntPtr mlu,
in uint LanguageCode, in uint CountryCode,
ref byte ObtainedLanguage_3, ref byte ObtainedCountry_3);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsMLUtranslationsCount(IntPtr mlu);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsMLUtranslationsCodes(IntPtr mlu,
uint idx,
ref byte LanguageCode_3,
ref byte CountryCode_3);
struct cmsUcrBg
{
IntPtr UcrToneCurve;
IntPtr BgToneCurve;
IntPtr DescMLU;
}
public const int cmsPRINTER_DEFAULT_SCREENS = 0x0001;
public const int cmsFREQUENCE_UNITS_LINES_CM = 0x0000;
public const int cmsFREQUENCE_UNITS_LINES_INCH = 0x0002;
public struct cmsDICTentry
{
public cmsDICTentry* Next;
IntPtr DisplayNameMLU;
IntPtr DisplayValueMLU;
byte* NameWcharT;
byte* ValueWcharT;
}
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsDictAlloc(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDictFree(IntPtr hDict);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsDictDup(IntPtr hDict);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsDictAddEntry(IntPtr hDict, void* Name, void* Value, IntPtr DisplayNameMLU, IntPtr DisplayValueMLU);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern cmsDICTentry* cmsDictGetEntryList(IntPtr hDict);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern cmsDICTentry* cmsDictNextEntry(cmsDICTentry* e);
// Access to Profile data ----------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateProfilePlaceholder(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetProfileContextID(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsGetTagCount(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern TagSignature cmsGetTagSignature(IntPtr hProfile, uint n);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsTag(IntPtr hProfile, TagSignature sig);
// Read and write pre-formatted data
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void* cmsReadTag(IntPtr hProfile, TagSignature sig);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsWriteTag(IntPtr hProfile, TagSignature sig, void* data);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsLinkTag(IntPtr hProfile, TagSignature sig, TagSignature dest);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern TagSignature cmsTagLinkedTo(IntPtr hProfile, TagSignature sig);
// Read and write raw data
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsReadRawTag(IntPtr hProfile, TagSignature sig, void* Buffer, uint BufferSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsWriteRawTag(IntPtr hProfile, TagSignature sig, in byte data, uint Size);
// Access header data
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern HeaderFlags cmsGetHeaderFlags(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsGetHeaderAttributes(IntPtr hProfile, out ulong Flags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsGetHeaderProfileID(IntPtr hProfile, out Guid ProfileID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsGetHeaderCreationDateTime(IntPtr hProfile, out CmsStructTm Dest);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetHeaderRenderingIntent(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderFlags(IntPtr hProfile, HeaderFlags Flags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetHeaderManufacturer(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderManufacturer(IntPtr hProfile, uint manufacturer);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetHeaderCreator(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetHeaderModel(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderModel(IntPtr hProfile, uint model);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderAttributes(IntPtr hProfile, ulong Flags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderProfileID(IntPtr hProfile, in Guid ProfileID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetHeaderRenderingIntent(IntPtr hProfile, uint RenderingIntent);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ColorSpaceSignature
cmsGetPCS(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetPCS(IntPtr hProfile, ColorSpaceSignature pcs);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ColorSpaceSignature
cmsGetColorSpace(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetColorSpace(IntPtr hProfile, ColorSpaceSignature sig);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ProfileClassSignature
cmsGetDeviceClass(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetDeviceClass(IntPtr hProfile, ProfileClassSignature sig);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetProfileVersion(IntPtr hProfile, double Version);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsGetProfileVersion(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetEncodedICCversion(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetEncodedICCversion(IntPtr hProfile, uint Version);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsIntentSupported(IntPtr hProfile, uint Intent, ProfileUsedDirection UsedDirection);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsMatrixShaper(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIsCLUT(IntPtr hProfile, uint Intent, ProfileUsedDirection UsedDirection);
// Translate form/to our notation to ICC
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern ColorSpaceSignature _cmsICCcolorSpace(int OurNotation);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int _cmsLCMScolorSpace(ColorSpaceSignature ProfileSpace);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsChannelsOf(ColorSpaceSignature ColorSpace);
// Build a suitable formatter for the colorspace of this profile. nBytes=1 means 8 bits, nBytes=2 means 16 bits.
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsFormatterForColorspaceOfProfile(IntPtr hProfile, uint nBytes, bool lIsFloat);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsFormatterForPCSOfProfile(IntPtr hProfile, uint nBytes, bool lIsFloat);
// Localized info
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetProfileInfo(IntPtr hProfile, InfoType Info,
in uint LanguageCode, in uint CountryCode,
void* Buffer, uint BufferSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetProfileInfoASCII(IntPtr hProfile, InfoType Info,
in uint LanguageCode, in uint CountryCode,
byte* Buffer, uint BufferSize);
// MD5 message digest --------------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsMD5computeID(IntPtr hProfile);
// IO handlers ----------------------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true, CharSet = CharSet.Ansi)]
public static extern IntPtr cmsOpenIOhandlerFromMem(IntPtr ContextID, void* Buffer, uint size, string AccessMode);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsOpenIOhandlerFromNULL(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetProfileIOhandler(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsCloseIOhandler(IntPtr io);
// Profile high level functions ------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsOpenProfileFromMem(void* MemPtr, uint dwSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsOpenProfileFromMemTHR(IntPtr ContextID, void* MemPtr, uint dwSize);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsOpenProfileFromIOhandlerTHR(IntPtr ContextID, IntPtr io);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsOpenProfileFromIOhandler2THR(IntPtr ContextID, IntPtr io, bool write);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsCloseProfile(IntPtr hProfile);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsSaveProfileToMem(IntPtr hProfile, void* MemPtr, ref uint BytesNeeded);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsSaveProfileToIOhandler(IntPtr hProfile, IntPtr io);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateRGBProfileTHR(IntPtr ContextID,
in CIExyY WhitePoint,
in CIExyYTRIPLE Primaries,
ref IntPtr TransferFunction3);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateRGBProfile(in CIExyY WhitePoint,
in CIExyYTRIPLE Primaries,
ref IntPtr TransferFunction);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateGrayProfileTHR(IntPtr ContextID,
in CIExyY WhitePoint,
IntPtr TransferFunction);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateGrayProfile(in CIExyY WhitePoint,
IntPtr TransferFunction);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLinearizationDeviceLinkTHR(IntPtr ContextID,
ColorSpaceSignature ColorSpace,
ref IntPtr TransferFunctions);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLinearizationDeviceLink(ColorSpaceSignature ColorSpace,
ref IntPtr TransferFunctions);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateInkLimitingDeviceLinkTHR(IntPtr ContextID,
ColorSpaceSignature ColorSpace, double Limit);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateInkLimitingDeviceLink(ColorSpaceSignature ColorSpace, double Limit);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLab2ProfileTHR(IntPtr ContextID, in CIExyY WhitePoint);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLab2Profile(in CIExyY WhitePoint);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLab4ProfileTHR(IntPtr ContextID, in CIExyY WhitePoint);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateLab4Profile(in CIExyY WhitePoint);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateXYZProfileTHR(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateXYZProfile();
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreate_sRGBProfileTHR(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreate_sRGBProfile();
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateBCHSWabstractProfileTHR(IntPtr ContextID,
uint nLUTPoints,
double Bright,
double Contrast,
double Hue,
double Saturation,
uint TempSrc,
uint TempDest);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateBCHSWabstractProfile(uint nLUTPoints,
double Bright,
double Contrast,
double Hue,
double Saturation,
uint TempSrc,
uint TempDest);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateNULLProfileTHR(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateNULLProfile();
// Converts a transform to a devicelink profile
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsTransform2DeviceLink(IntPtr hTransform, double Version, uint dwFlags);
// Call with NULL as parameters to get the intent count
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetSupportedIntents(uint nMax, uint* Codes, byte** Descriptions);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetSupportedIntentsTHR(IntPtr ContextID, uint nMax, uint* Codes, byte** Descriptions);
// Transforms ---------------------------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateTransformTHR(IntPtr ContextID,
IntPtr Input,
uint InputFormat,
IntPtr Output,
uint OutputFormat,
uint Intent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateTransform(IntPtr Input,
uint InputFormat,
IntPtr Output,
uint OutputFormat,
uint Intent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateProofingTransformTHR(IntPtr ContextID,
IntPtr Input,
uint InputFormat,
IntPtr Output,
uint OutputFormat,
IntPtr Proofing,
uint Intent,
uint ProofingIntent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateProofingTransform(IntPtr Input,
uint InputFormat,
IntPtr Output,
uint OutputFormat,
IntPtr Proofing,
uint Intent,
uint ProofingIntent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateMultiprofileTransformTHR(IntPtr ContextID,
ref IntPtr hProfiles,
uint nProfiles,
uint InputFormat,
uint OutputFormat,
uint Intent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateMultiprofileTransform(ref IntPtr hProfiles,
uint nProfiles,
uint InputFormat,
uint OutputFormat,
uint Intent,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsCreateExtendedTransform(IntPtr ContextID,
uint nProfiles, ref IntPtr hProfiles,
ref bool BPC,
ref uint Intents,
ref double AdaptationStates,
IntPtr hGamutProfile,
uint nGamutPCSposition,
uint InputFormat,
uint OutputFormat,
uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDeleteTransform(IntPtr hTransform);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDoTransform(IntPtr Transform,
void* InputBuffer,
void* OutputBuffer,
uint Size);
[Obsolete]
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDoTransformStride(IntPtr Transform, // Deprecated
void* InputBuffer,
void* OutputBuffer,
uint Size,
uint Stride);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsDoTransformLineStride(IntPtr Transform,
void* InputBuffer,
void* OutputBuffer,
uint PixelsPerLine,
uint LineCount,
uint BytesPerLineIn,
uint BytesPerLineOut,
uint BytesPerPlaneIn,
uint BytesPerPlaneOut);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetAlarmCodes(ref ushort NewAlarm_cmsMAXCHANNELS);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsGetAlarmCodes(ref ushort NewAlarm_cmsMAXCHANNELS);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsSetAlarmCodesTHR(IntPtr ContextID,
ref ushort NewAlarm_cmsMAXCHANNELS);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsGetAlarmCodesTHR(IntPtr ContextID,
ref ushort NewAlarm_cmsMAXCHANNELS);
// Adaptation state for absolute colorimetric intent
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsSetAdaptationState(double d);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsSetAdaptationStateTHR(IntPtr ContextID, double d);
// Grab the ContextID from an open transform. Returns NULL if a NULL transform is passed
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGetTransformContextID(IntPtr hTransform);
// Grab the input/output formats
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetTransformInputFormat(IntPtr hTransform);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetTransformOutputFormat(IntPtr hTransform);
// For backwards compatibility
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsChangeBuffersFormat(IntPtr hTransform,
uint InputFormat,
uint OutputFormat);
// PostScript ColorRenderingDictionary and ColorSpaceArray ----------------------------------------------------
// lcms2 unified method to access postscript color resources
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetPostScriptColorResource(IntPtr ContextID,
PSResourceType Type,
IntPtr hProfile,
uint Intent,
uint dwFlags,
IntPtr io);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetPostScriptCSA(IntPtr ContextID, IntPtr hProfile, uint Intent, uint dwFlags, void* Buffer, uint dwBufferLen);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsGetPostScriptCRD(IntPtr ContextID, IntPtr hProfile, uint Intent, uint dwFlags, void* Buffer, uint dwBufferLen);
// IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsIT8Alloc(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsIT8Free(IntPtr hIT8);
// Tables
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsIT8TableCount(IntPtr hIT8);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsIT8SetTable(IntPtr hIT8, uint nTable);
// Persistence
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsIT8LoadFromMem(IntPtr ContextID, void* Ptr, uint len);
//[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
//public static extern IntPtr cmsIT8LoadFromIOhandler(IntPtr ContextID, IntPtr io);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SaveToMem(IntPtr hIT8, void* MemPtr, uint* BytesNeeded);
// Properties
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string cmsIT8GetSheetType(IntPtr hIT8);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetSheetType(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Type);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetComment(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cComment);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetPropertyStr(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Str);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetPropertyDbl(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp, double Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetPropertyHex(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp, uint Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetPropertyMulti(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Key, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string SubKey, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Buffer);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetPropertyUncooked(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Key, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Buffer);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPUTF8Str)]
public static extern string cmsIT8GetProperty(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsIT8GetPropertyDbl(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPUTF8Str)]
public static extern string cmsIT8GetPropertyMulti(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Key, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string SubKey);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsIT8EnumProperties(IntPtr hIT8, out byte** PropertyNames);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern uint cmsIT8EnumPropertyMulti(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cProp, out byte** SubpropertyNames);
// Datasets
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPUTF8Str)]
public static extern string cmsIT8GetDataRowCol(IntPtr hIT8, int row, int col);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsIT8GetDataRowColDbl(IntPtr hIT8, int row, int col);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetDataRowCol(IntPtr hIT8, int row, int col,
[In, MarshalAs(UnmanagedType.LPUTF8Str)] string Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetDataRowColDbl(IntPtr hIT8, int row, int col,
double Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPUTF8Str)]
public static extern string cmsIT8GetData(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cPatch, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsIT8GetDataDbl(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cPatch, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetData(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cPatch,
[In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample,
[In, MarshalAs(UnmanagedType.LPUTF8Str)] string Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetDataDbl(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cPatch,
[In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample,
double Val);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsIT8FindDataFormat(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetDataFormat(IntPtr hIT8, int n, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Sample);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsIT8EnumDataFormat(IntPtr hIT8, out byte** SampleNames);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.LPUTF8Str)]
public static extern string cmsIT8GetPatchName(IntPtr hIT8, int nPatch, void* buffer);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsIT8GetPatchByName(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cPatch);
// The LABEL extension
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern int cmsIT8SetTableByLabel(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSet, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cField, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string ExpectedType);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsIT8SetIndexColumn(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string cSample);
// Formatter for double
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsIT8DefineDblFormat(IntPtr hIT8, [In, MarshalAs(UnmanagedType.LPUTF8Str)] string Formatter);
// Gamut boundary description routines ------------------------------------------------------------------------------
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern IntPtr cmsGBDAlloc(IntPtr ContextID);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern void cmsGBDFree(IntPtr hGBD);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsGDBAddPoint(IntPtr hGBD, in CIELab Lab);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsGDBCompute(IntPtr hGDB, uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsGDBCheckPoint(IntPtr hGBD, in CIELab Lab);
// Feature detection ----------------------------------------------------------------------------------------------
// Estimate the black point
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsDetectBlackPoint(out CIEXYZ BlackPoint, IntPtr hProfile, uint Intent, uint dwFlags);
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsDetectDestinationBlackPoint(out CIEXYZ BlackPoint, IntPtr hProfile, uint Intent, uint dwFlags);
// Estimate total area coverage
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsDetectTAC(IntPtr hProfile);
// Estimate gamma space, always positive. Returns -1 on error.
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern double cmsDetectRGBProfileGamma(IntPtr hProfile, double threshold);
// Poor man's gamut mapping
[DllImport(LIB, CallingConvention = CallingConvention.Winapi, ExactSpelling = true)]
public static extern bool cmsDesaturateLab(ref CIELab Lab,
double amax, double amin,
double bmax, double bmin);
}
}
+46
View File
@@ -0,0 +1,46 @@
using System;
namespace LittleCms
{
public abstract class CmsObject
{
private IntPtr handle;
private bool isOwner;
public IntPtr Handle => handle;
public bool IsOwner => isOwner;
public abstract CmsContext Context { get; }
protected CmsObject() { }
protected CmsObject(IntPtr handle, bool moveOwnership)
{
AttachObject(handle, isOwner);
}
protected void AttachObject(IntPtr handle, bool moveOwnership)
{
if (handle != IntPtr.Zero && isOwner)
{
FreeObject();
}
if (handle == IntPtr.Zero)
{
throw new NullReferenceException();
}
this.handle = handle;
this.isOwner = moveOwnership;
}
~CmsObject() {
if (handle != IntPtr.Zero && isOwner) {
FreeObject();
}
}
protected abstract void FreeObject();
}
}
+145
View File
@@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
namespace LittleCms
{
public struct CmsPixelFormat
{
public uint Value;
public static implicit operator uint(CmsPixelFormat x) => x.Value;
// boolean fields
const int shift_IsPremultipliedAlpha = 23;
const int shift_IsFloatingPoint = 22;
const int shift_IsOptimized = 21;
const int shift_RotateFirstChannel = 14;
const int shift_IsInvertedIntensity = 13;
const int shift_IsPlanar = 12;
const int shift_IsWordOrderSwapped = 11;
const int shift_IsByteOrderSwapped = 10;
// enum fields
const uint mask_PixelType = 0b11111u;
const int shift_PixelType = 16;
// uint fields
const uint mask_ExtraChannels = 0b111u;
const int shift_ExtraChannels = 7;
const uint mask_Channels = 0b1111u;
const int shift_Channels = 3;
const uint mask_BytesPerChannel = 0b111;
const int shift_BytesPerChannel = 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool GetBit(int shift)
{
var mask = 1u << shift;
return (Value & mask) != 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetBit(int shift, bool value)
{
var mask = 1u << shift;
Value = (Value & (~mask)) | (value ? mask : 0);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint GetBits(int shift, uint mask) => (Value >> shift) & mask;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetBits(int shift, uint mask, uint value)
{
var smask = mask << shift;
value = (value & mask) << shift;
Value = (Value & (~smask)) | value;
}
public bool IsPremultipliedAlpha
{
get => GetBit(shift_IsPremultipliedAlpha);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsPremultipliedAlpha, value);
}
public bool IsFloatingPoint
{
get => GetBit(shift_IsFloatingPoint);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsFloatingPoint, value);
}
public bool IsOptimized
{
get => GetBit(shift_IsOptimized);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsOptimized, value);
}
public bool RotateFirstChannel
{
get => GetBit(shift_RotateFirstChannel);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_RotateFirstChannel, value);
}
public bool IsPlanar
{
get => GetBit(shift_IsPlanar);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsPlanar, value);
}
public bool IsInvertedIntensity
{
get => GetBit(shift_IsInvertedIntensity);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsInvertedIntensity, value);
}
public bool IsWordOrderSwapped
{
get => GetBit(shift_IsWordOrderSwapped);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsWordOrderSwapped, value);
}
public bool IsByteOrderSwapped
{
get => GetBit(shift_IsByteOrderSwapped);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBit(shift_IsByteOrderSwapped, value);
}
public PixelType PixelType
{
get => (PixelType)GetBits(shift_PixelType, mask_PixelType);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBits(shift_PixelType, mask_PixelType, (uint)value);
}
public int ExtraChannels
{
get => (int)GetBits(shift_ExtraChannels, mask_ExtraChannels);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBits(shift_ExtraChannels, mask_ExtraChannels, (uint)value);
}
public int Channels
{
get => (int)GetBits(shift_Channels, mask_Channels);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBits(shift_Channels, mask_Channels, (uint)value);
}
/// <summary>
/// use 0 for double-precision IEEE-754 floating point.
/// </summary>
public int BytesPerChannel
{
get => (int)GetBits(shift_BytesPerChannel, mask_BytesPerChannel);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set => SetBits(shift_BytesPerChannel, mask_BytesPerChannel, (uint)value);
}
public static CmsPixelFormat RGB8 => new CmsPixelFormat { PixelType = PixelType.RGB, Channels = 3, BytesPerChannel = 1 };
public static CmsPixelFormat RGBDouble => new CmsPixelFormat { PixelType = PixelType.RGB, Channels = 3, BytesPerChannel = 0, IsFloatingPoint = true };
public static CmsPixelFormat XYZDouble => new CmsPixelFormat { PixelType = PixelType.XYZ, Channels = 3, BytesPerChannel = 0, IsFloatingPoint = true };
}
}
+52
View File
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Text;
using static LittleCms.CmsNative;
namespace LittleCms
{
public class CmsTransform : CmsObject
{
private IccProfile inputProfile;
private IccProfile outputProfile;
public override CmsContext Context { get; }
public CmsTransform(IccProfile inputProfile, CmsPixelFormat inputFormat,
IccProfile outputProfile, CmsPixelFormat outputFormat,
RenderingIntent intent, TransformFlags flags) : this(CmsContext.Default, inputProfile, inputFormat, outputProfile, outputFormat, intent, flags) { }
public CmsTransform(CmsContext context, IccProfile inputProfile, CmsPixelFormat inputFormat,
IccProfile outputProfile, CmsPixelFormat outputFormat,
RenderingIntent intent, TransformFlags flags)
{
this.Context = context;
this.inputProfile = inputProfile;
this.outputProfile = outputProfile;
var handle = CheckError(cmsCreateTransformTHR(context.Handle, inputProfile.Handle, inputFormat, outputProfile.Handle, outputFormat, (uint)intent, (uint)flags));
AttachObject(handle, true);
}
protected override void FreeObject()
{
cmsDeleteTransform(Handle);
}
public unsafe void DoTransform<TIn, TOut>(ReadOnlySpan<TIn> input, Span<TOut> output, uint pixelsToTransform) where TIn : unmanaged where TOut : unmanaged
{
fixed (TIn* inptr = input)
fixed (TOut* outptr = output)
cmsDoTransform(Handle, inptr, outptr, pixelsToTransform);
}
public unsafe void DoTransform<TIn, TOut>(ReadOnlySpan<TIn> input, Span<TOut> output, uint pixelsPerLine, uint lineCount,
uint bytesPerLineIn, uint bytesPerLineOut, uint bytesPerPlaneIn, uint bytesPerPlaneOut) where TIn : unmanaged where TOut : unmanaged
{
fixed (TIn* inptr = input)
fixed (TOut* outptr = output)
cmsDoTransformLineStride(Handle, inptr, outptr, pixelsPerLine, lineCount, bytesPerLineIn, bytesPerLineOut, bytesPerPlaneIn, bytesPerPlaneOut);
}
}
}
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
namespace LittleCms.Data
{
internal record BlittableDataTag<T>(TagSignature TagSignature) : ISafeTagSignature<T> where T : unmanaged
{
public unsafe T ReadFromProfile(IccProfile profile)
{
var ptr = profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
return *(T*)ptr;
}
public unsafe void WriteToProfile(IccProfile profile, T data)
{
profile.WriteTag(TagSignature, &data);
}
}
}
@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
internal class ChromaticAdaptationTagSignature : ISafeTagSignature<double[,]>
{
public TagSignature TagSignature => TagSignature.ChromaticAdaptationTag;
public unsafe double[,] ReadFromProfile(IccProfile profile)
{
var ptr = (double*)profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
var result = new double[3, 3];
new ReadOnlySpan<double>(ptr, 9).CopyTo(MemoryMarshal.CreateSpan(ref result[0, 0], 9));
return result;
}
public unsafe void WriteToProfile(IccProfile profile, double[,] data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
fixed (double* ptr = &data[0, 0])
profile.WriteTag(TagSignature, ptr);
}
}
}
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
public interface ISafeTagSignature<T>
{
TagSignature TagSignature { get; }
T ReadFromProfile(IccProfile profile);
void WriteToProfile(IccProfile profile, T data);
}
}
+32
View File
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
internal record IccDataTagSignature(TagSignature TagSignature) : ISafeTagSignature<ICCData>
{
public unsafe ICCData ReadFromProfile(IccProfile profile)
{
var ptr = profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
return _ICCData.ToManaged((_ICCData*)ptr);
}
public unsafe void WriteToProfile(IccProfile profile, ICCData obj)
{
var data = new byte[sizeof(_ICCDataHeader) + obj.Data.Length];
obj.Data.Span.CopyTo(data.AsSpan().Slice(sizeof(_ICCDataHeader)));
fixed (byte* ptr = data)
{
var header = (_ICCDataHeader*)ptr;
header->flag = obj.Flag;
header->len = (uint)obj.Data.Length;
profile.WriteTag(TagSignature, ptr);
}
}
}
}
+111
View File
@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.ComponentModel;
using static LittleCms.CmsNative;
using System.Runtime.CompilerServices;
namespace LittleCms.Data
{
public class MLU : CmsObject
{
public const string NoLanguage = "\0\0";
public const string NoCountry = "\0\0";
#pragma warning disable CS0809 // Obsolete member overrides non-obsolete member
[Obsolete("Only allocator in context is used in MLU, which is not modeled in this binding")]
public override CmsContext Context { get; } = CmsContext.Default;
#pragma warning restore CS0809
internal unsafe static (uint, uint) EncodeLanguageCountryCode(string code1, string code2)
{
if (code1 == null || code1.Length != 2 || code1[0] >= 0x80 || code1[1] >= 0x80) throw new ArgumentException();
if (code2 == null || code2.Length != 2 || code2[0] >= 0x80 || code2[1] >= 0x80) throw new ArgumentException();
Span<uint> result32 = stackalloc uint[2];
var result8 = MemoryMarshal.Cast<uint, byte>(result32);
result8[0] = (byte)code1[0];
result8[1] = (byte)code1[1];
result8[4] = (byte)code2[0];
result8[5] = (byte)code2[1];
return (result32[0], result32[1]);
}
/// <summary>
/// Attach a cmsMLU
/// </summary>
/// <param name="handle"></param>
/// <param name="moveOwnership"></param>
public MLU(IntPtr handle, bool moveOwnership = false) : base(handle, moveOwnership)
{
// FIXME: missing cmsGetMLUContextID
}
public MLU(int itemCount) : this(CmsContext.Default, itemCount) { }
public MLU(CmsContext context, int itemCount)
{
Context = context;
IntPtr handle = CheckError(cmsMLUalloc(context.Handle, (uint)itemCount));
AttachObject(handle, true);
}
/// <summary>
/// Copy a cmsMLU by handle
/// </summary>
/// <param name="handle"></param>
/// <exception cref="NotImplementedException"></exception>
public static MLU CopyFromObject(nint handleToCopy)
{
nint handle = CheckError(cmsMLUdup(handleToCopy));
return new(handle, true);
}
/// <summary>
/// Creates a MLU with en-US string
/// </summary>
/// <param name="s"></param>
public MLU(string s) : this(1)
{
Set("en", "US", s);
}
protected override void FreeObject()
{
cmsMLUfree(Handle);
}
public unsafe void Set(string language, string country, string s)
{
var (lang, cont) = EncodeLanguageCountryCode(language, country);
var wchbuf = WcharEncoding.GetBytes(s + "\0");
CheckError(cmsMLUsetWide(Handle, in lang, in cont, in wchbuf[0]));
}
public unsafe string? Get(string languageCode, string countryCode)
{
var (lang, cont) = EncodeLanguageCountryCode(languageCode, countryCode);
var len = cmsMLUgetWide(Handle, in lang, in cont, null, 0);
if (len == 0)
{
return null;
}
var buf = new byte[(int)len];
fixed (byte* ptr = buf)
len = CheckError(cmsMLUgetWide(Handle, in lang, in cont, ptr, len));
var s = WcharEncoding.GetString(buf);
return s[..^1];
}
public string? GetAny() => Get(NoLanguage, NoCountry);
public override string ToString() => GetAny() ?? "";
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
internal record MluTagSignature(TagSignature TagSignature) : ISafeTagSignature<MLU>
{
public unsafe MLU ReadFromProfile(IccProfile profile)
{
var ptr = profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
return MLU.CopyFromObject((IntPtr)ptr);
}
public unsafe void WriteToProfile(IccProfile profile, MLU data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
profile.WriteTag(TagSignature, (void*)data.Handle);
}
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace LittleCms.Data
{
public record RgbToneCurve(ToneCurve Red, ToneCurve Green, ToneCurve Blue)
{
public ToneCurve[] ToArray() => new[] { Red, Green, Blue };
}
}
+41
View File
@@ -0,0 +1,41 @@
namespace LittleCms.Data
{
public static partial class SafeTagSignature
{
public static ISafeTagSignature<CIExyYTRIPLE> ChromaticityTag { get; } = new BlittableDataTag<CIExyYTRIPLE>(TagSignature.ChromaticityTag);
public static ISafeTagSignature<CIEXYZ> MediaWhitePointTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.MediaWhitePointTag);
public static ISafeTagSignature<CIEXYZ> BlueColorantTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.BlueColorantTag);
public static ISafeTagSignature<CIEXYZ> GreenColorantTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.GreenColorantTag);
public static ISafeTagSignature<CIEXYZ> LuminanceTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.LuminanceTag);
public static ISafeTagSignature<CIEXYZ> MediaBlackPointTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.MediaBlackPointTag);
public static ISafeTagSignature<CIEXYZ> RedColorantTag { get; } = new BlittableDataTag<CIEXYZ>(TagSignature.RedColorantTag);
public static ISafeTagSignature<double[,]> ChromaticAdaptationTag { get; } = new ChromaticAdaptationTagSignature();
public static ISafeTagSignature<ICCData> Mhc2Tag { get; } = new IccDataTagSignature(TagSignature.MHC2);
public static ISafeTagSignature<ICCData> DataTag { get; } = new IccDataTagSignature(TagSignature.DataTag);
public static ISafeTagSignature<ICCData> Ps2CRD0Tag { get; } = new IccDataTagSignature(TagSignature.Ps2CRD0Tag);
public static ISafeTagSignature<ICCData> Ps2CRD1Tag { get; } = new IccDataTagSignature(TagSignature.Ps2CRD1Tag);
public static ISafeTagSignature<ICCData> Ps2CRD2Tag { get; } = new IccDataTagSignature(TagSignature.Ps2CRD2Tag);
public static ISafeTagSignature<ICCData> Ps2CRD3Tag { get; } = new IccDataTagSignature(TagSignature.Ps2CRD3Tag);
public static ISafeTagSignature<ICCData> Ps2CSATag { get; } = new IccDataTagSignature(TagSignature.Ps2CSATag);
public static ISafeTagSignature<ICCData> Ps2RenderingIntentTag { get; } = new IccDataTagSignature(TagSignature.Ps2RenderingIntentTag);
public static ISafeTagSignature<ToneCurve> BlueTRCTag { get; } = new ToneCurveTagSignature(TagSignature.BlueTRCTag);
public static ISafeTagSignature<ToneCurve> GrayTRCTag { get; } = new ToneCurveTagSignature(TagSignature.GrayTRCTag);
public static ISafeTagSignature<ToneCurve> GreenTRCTag { get; } = new ToneCurveTagSignature(TagSignature.GreenTRCTag);
public static ISafeTagSignature<ToneCurve> RedTRCTag { get; } = new ToneCurveTagSignature(TagSignature.RedTRCTag);
public static ISafeTagSignature<RgbToneCurve> VcgtTag { get; } = new VideoCardGammaTableTagSignature();
public static ISafeTagSignature<MLU> CharTargetTag { get; } = new MluTagSignature(TagSignature.CharTargetTag);
public static ISafeTagSignature<MLU> CopyrightTag { get; } = new MluTagSignature(TagSignature.CopyrightTag);
public static ISafeTagSignature<MLU> DeviceMfgDescTag { get; } = new MluTagSignature(TagSignature.DeviceMfgDescTag);
public static ISafeTagSignature<MLU> DeviceModelDescTag { get; } = new MluTagSignature(TagSignature.DeviceModelDescTag);
public static ISafeTagSignature<MLU> ProfileDescriptionTag { get; } = new MluTagSignature(TagSignature.ProfileDescriptionTag);
public static ISafeTagSignature<MLU> ScreeningDescTag { get; } = new MluTagSignature(TagSignature.ScreeningDescTag);
public static ISafeTagSignature<MLU> ViewingCondDescTag { get; } = new MluTagSignature(TagSignature.ViewingCondDescTag);
}
}
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
public class TagNotFoundException : Exception
{
private const int hr = unchecked((int)0x80131577);
public TagNotFoundException()
{
HResult = hr;
}
public TagNotFoundException(string message) : base(message)
{
HResult = hr;
}
public TagNotFoundException(string? message, Exception? innerException)
: base(message, innerException)
{
HResult = hr;
}
}
}
+65
View File
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Text;
using static LittleCms.CmsNative;
namespace LittleCms.Data
{
public class ToneCurve : CmsObject
{
#pragma warning disable CS0809
[Obsolete("lcms2 don't expose API to get context of cmsToneCurve")]
public override CmsContext Context => throw new NotImplementedException();
#pragma warning restore CS0809
public ToneCurve(nint handle, bool moveOwnership) : base(handle, moveOwnership) { }
public ToneCurve(CmsContext context, double gamma)
{
AttachObject(CheckError(cmsBuildGamma(context.Handle, gamma)), true);
}
public ToneCurve(CmsContext context, ReadOnlySpan<float> table)
{
AttachObject(CheckError(cmsBuildTabulatedToneCurveFloat(context.Handle, (uint)table.Length, in table[0])), true);
}
public ToneCurve(CmsContext context, ReadOnlySpan<ushort> table)
{
AttachObject(CheckError(cmsBuildTabulatedToneCurve16(context.Handle, (uint)table.Length, in table[0])), true);
}
public ToneCurve(double gamma) : this(CmsContext.Default, gamma) { }
public ToneCurve(ReadOnlySpan<float> table) : this(CmsContext.Default, table) { }
public ToneCurve(ReadOnlySpan<ushort> table) : this(CmsContext.Default, table) { }
public static ToneCurve CopyFromObject(nint copyFromObject)
{
var handle = CheckError(cmsDupToneCurve(copyFromObject));
return new(handle, true);
}
protected override void FreeObject()
{
throw new NotImplementedException();
}
public ToneCurve Reverse()
{
var handle = CheckError(cmsReverseToneCurve(Handle));
return new(handle, true);
}
public ToneCurve Reverse(uint sampleCount)
{
var handle = CheckError(cmsReverseToneCurveEx(sampleCount, Handle));
return new(handle, true);
}
public ushort EvalU16(ushort x) => cmsEvalToneCurve16(Handle, x);
public float EvalF32(float x) => cmsEvalToneCurveFloat(Handle, x);
}
}
+24
View File
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
internal record ToneCurveTagSignature(TagSignature TagSignature) : ISafeTagSignature<ToneCurve>
{
public unsafe ToneCurve ReadFromProfile(IccProfile profile)
{
var ptr = profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
return ToneCurve.CopyFromObject((IntPtr)ptr);
}
public unsafe void WriteToProfile(IccProfile profile, ToneCurve data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
profile.WriteTag(TagSignature, (void*)data.Handle);
}
}
}
@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LittleCms.Data
{
internal class VideoCardGammaTableTagSignature : ISafeTagSignature<RgbToneCurve>
{
public TagSignature TagSignature => TagSignature.VcgtTag;
public unsafe RgbToneCurve ReadFromProfile(IccProfile profile)
{
var ptr = (IntPtr*)profile.ReadTag(TagSignature);
if (ptr == null) throw new TagNotFoundException();
return new RgbToneCurve(
ToneCurve.CopyFromObject(ptr[0]),
ToneCurve.CopyFromObject(ptr[1]),
ToneCurve.CopyFromObject(ptr[2])
);
}
public unsafe void WriteToProfile(IccProfile profile, RgbToneCurve obj)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
var handles = stackalloc IntPtr[] { obj.Red.Handle, obj.Green.Handle, obj.Blue.Handle };
profile.WriteTag(TagSignature, handles);
}
}
}
+259
View File
@@ -0,0 +1,259 @@
using LittleCms.Data;
using System;
using System.Diagnostics.CodeAnalysis;
using static LittleCms.CmsNative;
namespace LittleCms
{
public class IccProfile : CmsObject
{
public override CmsContext Context { get; }
public byte[] RawBytes { get; }
public IccProfile(IntPtr handle, bool moveOwnership) : base(handle, moveOwnership)
{
Context = CmsContext.GetFromHandle(cmsGetProfileContextID(handle));
}
public IccProfile(IntPtr handle, CmsContext context, ReadOnlySpan<byte> bytes) : base(handle, true)
{
Context = context;
RawBytes = bytes.ToArray();
}
public static IccProfile Create_sRGB(CmsContext context)
{
return new IccProfile(CheckError(cmsCreate_sRGBProfileTHR(context.Handle)), true);
}
public static IccProfile Create_sRGB()
{
return new IccProfile(CheckError(cmsCreate_sRGBProfile()), true);
}
public static IccProfile CreateXYZ(CmsContext context)
{
return new IccProfile(CheckError(cmsCreateXYZProfileTHR(context.Handle)), true);
}
public static IccProfile CreateXYZ()
{
return new IccProfile(CheckError(cmsCreateXYZProfile()), true);
}
public static IccProfile CreateRGB(CIExyY whitepoint, CIExyYTRIPLE primaries, RgbToneCurve transfer)
{
return CreateRGB(CmsContext.Default, whitepoint, primaries, transfer);
}
public static unsafe IccProfile CreateRGB(CmsContext context, CIExyY whitepoint, CIExyYTRIPLE primaries, RgbToneCurve transfer)
{
Span<IntPtr> transferhandle = stackalloc IntPtr[] { transfer.Red.Handle, transfer.Green.Handle, transfer.Blue.Handle };
var handle = CheckError(cmsCreateRGBProfile(whitepoint, primaries, ref transferhandle[0]));
return new(handle, true);
}
public static unsafe IccProfile Open(CmsContext context, ReadOnlySpan<byte> bytes)
{
fixed (byte* ptr = bytes)
{
var handle = CheckError(cmsOpenProfileFromMemTHR(context.Handle, ptr, (uint)bytes.Length));
return new IccProfile(handle, context, bytes);
}
}
public static IccProfile Open(ReadOnlySpan<byte> bytes) => Open(CmsContext.Default, bytes);
public unsafe byte[] GetBytes()
{
uint newlen = 0;
CheckError(cmsSaveProfileToMem(Handle, null, ref newlen));
var newicc = new byte[newlen];
fixed (byte* ptr = newicc)
CheckError(cmsSaveProfileToMem(Handle, ptr, ref newlen));
return newicc;
}
protected override void FreeObject()
{
cmsCloseProfile(Handle);
}
public unsafe void* ReadTag(TagSignature sig)
{
return cmsReadTag(Handle, sig);
}
public unsafe bool ContainsTag(TagSignature sig) => ReadTag(sig) != null;
public T ReadTag<T>(ISafeTagSignature<T> sig)
{
return sig.ReadFromProfile(this);
}
[return: NotNullIfNotNull(nameof(defaultValue))]
public T? ReadTagOrDefault<T>(ISafeTagSignature<T> sig, T? defaultValue = default)
{
try
{
return sig.ReadFromProfile(this);
}
catch (TagNotFoundException)
{
return defaultValue;
}
}
public bool TryReadTag<T>(ISafeTagSignature<T> sig, [NotNullWhen(returnValue: true)] out T? value)
{
try
{
value = sig.ReadFromProfile(this)!;
return true;
}
catch (TagNotFoundException)
{
value = default;
return false;
}
}
public unsafe void WriteTag(TagSignature sig, void* handle)
{
var result = cmsWriteTag(Handle, sig, handle);
if (handle != null)
{
CheckError(result);
}
}
public void WriteTag<T>(ISafeTagSignature<T> sig, T value)
{
sig.WriteToProfile(this, value);
}
public unsafe void RemoveTag(TagSignature sig)
{
CheckError(cmsWriteTag(Handle, sig, null));
}
public unsafe void RemoveTag<T>(ISafeTagSignature<T> sig)
{
CheckError(cmsWriteTag(Handle, sig.TagSignature, null));
}
public unsafe byte[]? ReadRawTag(TagSignature sig)
{
if (!cmsIsTag(Handle, sig)) return null;
var len = cmsReadRawTag(Handle, sig, null, 0);
var buf = new byte[len];
fixed (byte* ptr = buf)
len = cmsReadRawTag(Handle, sig, ptr, len);
return buf;
}
public void WriteRawTag(TagSignature sig, ReadOnlySpan<byte> bytes)
{
CheckError(cmsWriteRawTag(Handle, sig, in bytes[0], (uint)bytes.Length));
}
public void ComputeProfileId()
{
CheckError(cmsMD5computeID(Handle));
}
public HeaderFlags HeaderFlags
{
get => cmsGetHeaderFlags(Handle);
set => cmsSetHeaderFlags(Handle, value);
}
public unsafe string GetInfo(InfoType info, string languageCode = MLU.NoLanguage, string countryCode = MLU.NoCountry)
{
var (lang, cont) = MLU.EncodeLanguageCountryCode(languageCode, countryCode);
var len = cmsGetProfileInfo(Handle, info, in lang, in cont, null, 0);
if (len == 0) return "";
var buf = new byte[len];
fixed (byte* ptr = buf)
len = cmsGetProfileInfo(Handle, info, in lang, in cont, ptr, len);
var s = WcharEncoding.GetString(buf)[..^1];
return s;
}
public ulong HeaderAttributes
{
get
{
cmsGetHeaderAttributes(Handle, out var attr);
return attr;
}
set => cmsSetHeaderAttributes(Handle, value);
}
public Guid HeaderProfileId
{
get
{
cmsGetHeaderProfileID(Handle, out var attr);
return attr;
}
set => cmsSetHeaderProfileID(Handle, value);
}
public DateTime HeaderCreationDateTime
{
get
{
CheckError(cmsGetHeaderCreationDateTime(Handle, out var tm));
return tm.ToDateTime();
}
}
public RenderingIntent HeaderRenderingIntent
{
get => (RenderingIntent)cmsGetHeaderRenderingIntent(Handle);
set => cmsSetHeaderRenderingIntent(Handle, (uint)value);
}
public uint HeaderManufacturer
{
get => cmsGetHeaderManufacturer(Handle);
set => cmsSetHeaderManufacturer(Handle, value);
}
public uint HeaderCreator
{
get => cmsGetHeaderCreator(Handle);
}
public uint HeaderModel
{
get => cmsGetHeaderModel(Handle);
set => cmsSetHeaderModel(Handle, value);
}
public ColorSpaceSignature PCS
{
get => cmsGetPCS(Handle);
set => cmsSetPCS(Handle, value);
}
public ColorSpaceSignature ColorSpace
{
get => cmsGetColorSpace(Handle);
set => cmsSetColorSpace(Handle, value);
}
public ProfileClassSignature DeviceClass
{
get => cmsGetDeviceClass(Handle);
set => cmsSetDeviceClass(Handle, value);
}
public double ProfileVersion
{
get => cmsGetProfileVersion(Handle);
set => cmsSetProfileVersion(Handle, value);
}
}
}
+16
View File
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<LangVersion>9</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Update="lcms2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Binary file not shown.
+96
View File
@@ -0,0 +1,96 @@
using System;
namespace MHC2Gen;
public static class CmsFunctions
{
private const double PQ_M1 = 0.1593017578125;
private const double PQ_M2 = 78.84375;
private const double PQ_C1 = 0.8359375;
private const double PQ_C2 = 18.8515625;
private const double PQ_C3 = 18.6875;
public static double PqEotf(double signalValue)
{
var signalValuePow = Math.Pow(signalValue, 1D / PQ_M2);
var value1 = Math.Max(signalValuePow - PQ_C1, 0);
var value2 = PQ_C2 - PQ_C3 * signalValuePow;
var luminance = 10000D * Math.Pow(value1 / value2, 1D / PQ_M1);
return luminance;
}
public static double InvPqEotf(double luminance)
{
var normalizedLuminance = luminance / 10000D;
var luminancePow = Math.Pow(normalizedLuminance, PQ_M1);
var value1 = PQ_C1 + PQ_C2 * luminancePow;
var value2 = 1 + PQ_C3 * luminancePow;
var signalValue = Math.Pow(value1 / value2, PQ_M2);
return signalValue;
}
public static double SrgbInvEotf(double luminance, double whiteLuminance, double blackLuminance, double gamma = 2.4)
{
//const double X1 = 0.0404482362771082;
const double X2 = 0.00313066844250063;
var x = whiteLuminance <= blackLuminance ? 0 : (luminance - blackLuminance) / (whiteLuminance - blackLuminance);
if (x > 1)
{
x = 1;
}
else if (x < 0)
{
x = 0;
}
else if (x <= X2)
{
x *= 12.92;
}
else
{
x = 1.055 * Math.Pow(x, 1 / gamma) - 0.055;
}
return x;
}
public static (double, double) SrgbAcm(double signalValue, double whiteLuminance, double blackLuminance = 0, double gamma = 2.2, int divider = 1023)
{
var b = signalValue / divider;
var c = PqEotf(b);
var d = SrgbInvEotf(c, whiteLuminance, blackLuminance);
var e = blackLuminance + (whiteLuminance - blackLuminance) * Math.Pow(d, gamma);
var f = InvPqEotf(Math.Max(0, e));
var x = f + Math.Min(1, c / whiteLuminance) * (b - f);
return (b, x);
}
public static double Srgb(double signalValue)
{
var b = signalValue / 1023;
return b;
}
public static double GammaRgb(double signalValue, double gamma = 2.4)
{
return signalValue <= 0.0031308 ? signalValue * 12.92 : 1.055 * Math.Pow(signalValue, 1.0 / gamma) - 0.055;
}
public static double RgbToLinear(double signalValue, double gamma = 2.4)
{
return signalValue <= 0.04045 ? signalValue / 12.92 : Math.Pow((signalValue + 0.055) / 1.055, gamma);
}
}
+24
View File
@@ -0,0 +1,24 @@
using LittleCms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MHC2Gen
{
internal static class Extension
{
public static CIExy ToXY(in this CIExyY xyY)
{
return new() { x = xyY.x, y = xyY.y };
}
public static CIExy ToXY(in this CIEXYZ xyz)
{
return xyz.ToCIExyY().ToXY();
}
}
}
+29
View File
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MHC2Gen
{
internal class ST2084
{
private const double m1 = 2610.0 / 16384.0;
private const double m2 = 128.0 * 2523.0 / 4096.0;
private const double c1 = 3424.0 / 4096.0;
private const double c2 = 32.0 * 2413.0 / 4096.0;
private const double c3 = 32.0 * 2392.0 / 4096.0;
public static double NitsToSignal(double nits)
{
var Ypowm1 = Math.Pow(nits / 10000.0, m1);
return Math.Pow((c1 + c2 * Ypowm1) / (1 + c3 * Ypowm1), m2);
}
public static double SignalToNits(double signal)
{
var Epow1divm2 = Math.Pow(signal, 1.0 / m2);
return 10000 * Math.Pow(Math.Max(Epow1divm2 - c1, 0) / (c2 - c3 * Epow1divm2), 1 / m1);
}
}
}
File diff suppressed because it is too large. Load diff
+55
View File
@@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MathNet.Numerics" Version="4.15.0" />
</ItemGroup>
<PropertyGroup Condition="$([MSBuild]::VersionGreaterThanOrEquals('$(NETCoreSdkVersion)', '7.0'))">
<TargetFramework>net7.0</TargetFramework>
<UseNativeAot Condition="$(PublishDir) != ''">true</UseNativeAot>
</PropertyGroup>
<!-- Optimize for size -->
<PropertyGroup Condition="'$(UseNativeAot)' == 'true'">
<PublishAot>true</PublishAot>
<IlcOptimizationPreference>Size</IlcOptimizationPreference>
<IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
<IlcGenerateStackTraceData>true</IlcGenerateStackTraceData>
<TrimUnusedDependencies>true</TrimUnusedDependencies>
<RootPackageReference>false</RootPackageReference>
<TrimMode>full</TrimMode>
<!--DebuggerSupport Condition="'$(Configuration)'=='Release'">false</DebuggerSupport-->
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
<EnableUnsafeUTF7Encoding>false</EnableUnsafeUTF7Encoding>
<HttpActivityPropagationSupport>false</HttpActivityPropagationSupport>
<InvariantGlobalization>true</InvariantGlobalization>
<!--UseSystemResourceKeys>true</UseSystemResourceKeys-->
</PropertyGroup>
<ItemGroup Condition="'$(UseNativeAot)' != 'true'">
<None Include="$(SolutionDir)LittleCms\lcms2.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup Condition="'$(UseNativeAot)' == 'true'">
<DirectPInvoke Include="lcms2" />
<NativeLibrary Include="$(MSBuildThisFileDirectory)..\lcms2\Projects\VC2022\lcms2_static\$(Configuration)\x64\lcms2_static.lib" Condition="$(RuntimeIdentifier.StartsWith('win')) and $(RuntimeIdentifier.EndsWith('x64'))" />
<NativeLibrary Include="$(MSBuildThisFileDirectory)..\lcms2\Projects\VC2022\lcms2_static\$(Configuration)\ARM64\lcms2_static.lib" Condition="$(RuntimeIdentifier.StartsWith('win')) and $(RuntimeIdentifier.EndsWith('arm64'))" />
</ItemGroup>
<ItemGroup>
<None Remove="C:\Users\vinni\source\repos\ColorControl\LittleCms\lcms2.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LittleCms\LittleCms.csproj" />
</ItemGroup>
</Project>
+26
View File
@@ -0,0 +1,26 @@
using LittleCms;
namespace MHC2Gen;
public class MHC2Wrapper
{
public enum NamedGamut
{
sRGB,
AdobeRGB,
P3D65,
BT2020
}
public static byte[] GenerateSdrAcmProfile(GenerateProfileCommand command)
{
var profile = IccProfile.Create_sRGB();
var context = new DeviceIccContext(profile);
var newProfile = context.CreateIcc(command);
return newProfile.GetBytes();
}
}
+52
View File
@@ -0,0 +1,52 @@
using LittleCms;
namespace MHC2Gen
{
public class CIExy
{
public double x { get; set; }
public double y { get; set; }
public CIExy() { }
public CIExy(CIExy source)
{
x = source.x;
y = source.y;
}
public CIEXYZ ToXYZ(double Y = 1.0)
{
return new() { X = x * Y / y, Y = Y, Z = (1 - x - y) * Y / y };
}
}
public class RgbPrimaries
{
public CIExy Red { get; }
public CIExy Green { get; }
public CIExy Blue { get; }
public CIExy White { get; }
public RgbPrimaries(RgbPrimaries primaries)
{
Red = new CIExy(primaries.Red);
Green = new CIExy(primaries.Green);
Blue = new CIExy(primaries.Blue);
White = new CIExy(primaries.White);
}
public RgbPrimaries(CIExy red, CIExy green, CIExy blue, CIExy white)
{
Red = red;
Green = green;
Blue = blue;
White = white;
}
public static RgbPrimaries sRGB { get; } = new RgbPrimaries(new() { x = 0.64, y = 0.33 }, new() { x = 0.30, y = 0.60 }, new() { x = 0.15, y = 0.06 }, new() { x = 0.3127, y = 0.3290 });
public static RgbPrimaries AdobeRGB { get; } = new RgbPrimaries(new() { x = 0.64, y = 0.33 }, new() { x = 0.21, y = 0.71 }, new() { x = 0.15, y = 0.06 }, new() { x = 0.3127, y = 0.3290 });
public static RgbPrimaries P3D65 { get; } = new RgbPrimaries(new() { x = 0.68, y = 0.32 }, new() { x = 0.265, y = 0.690 }, new() { x = 0.15, y = 0.06 }, new() { x = 0.3127, y = 0.3290 });
public static RgbPrimaries Rec2020 { get; } = new RgbPrimaries(new() { x = 0.708, y = 0.292 }, new() { x = 0.170, y = 0.797 }, new() { x = 0.131, y = 0.046 }, new() { x = 0.3127, y = 0.3290 });
}
}
+38
View File
@@ -0,0 +1,38 @@
using System.ComponentModel;
namespace MHC2Gen;
public enum SDRTransferFunction
{
[Description("BT.1886")]
BT_1886 = 0,
[Description("Pure Power")]
PurePower = 1,
[Description("Piecewise")]
Piecewise = 2
}
public enum ColorGamut
{
Native = 0,
sRGB = 1,
P3 = 2,
Rec2020 = 3,
AdobeRGB = 4
}
public class GenerateProfileCommand
{
public string Description { get; set; }
public RgbPrimaries DevicePrimaries { get; set; } = new RgbPrimaries(RgbPrimaries.sRGB);
public SDRTransferFunction SDRTransferFunction { get; set; }
public ColorGamut ColorGamut { get; set; }
public double MinCLL { get; set; }
public double MaxCLL { get; set; }
public double WhiteLuminance { get; set; } = 2000;
public double BlackLuminance { get; set; } = 0;
public double SDRMinBrightness { get; set; } = 0;
public double SDRMaxBrightness { get; set; } = 100;
public double SDRBrightnessBoost { get; set; } = 0;
public double Gamma { get; set; } = 2.2;
}
+5
View File
@@ -206,6 +206,11 @@ The best and suggested method to provide this is via a Windows Service. Only whe
return name.Substring(0, 1).ToUpper() + name.Substring(1);
}
public static Dictionary<T, string> EnumToDictionary<T>() where T : struct, Enum
{
return Enum.GetValues<T>().ToDictionary(k => k, e => e.GetDescription());
}
public static string GetDescriptionByEnumName<T>(string value) where T : IConvertible
{
string description = null;
@@ -0,0 +1,26 @@
namespace ColorControl.Shared.Contracts.NVIDIA;
public class NvColorEnhancementSettings
{
public int DigitalVibranceLevel { get; set; } = 50;
public int HueAngle { get; set; }
public NvColorEnhancementSettings()
{
}
public NvColorEnhancementSettings(NvColorEnhancementSettings settings)
{
settings ??= new NvColorEnhancementSettings();
DigitalVibranceLevel = settings.DigitalVibranceLevel;
HueAngle = settings.HueAngle;
}
public override string ToString()
{
var value = string.Empty;
return value;
}
}
+10
View File
@@ -250,6 +250,16 @@ namespace ColorControl.Shared.Forms
return item;
}
public static void BuildMenuItemAndSub(ToolStripMenuItem parent, string text, string subText, EventHandler onClick)
{
var itemName = $"{parent.Name}_{text}";
var menuItem = BuildMenuItem(parent.DropDownItems, itemName, text);
var subItemName = $"{itemName}_SubItem";
var subItem = BuildMenuItem(menuItem.DropDownItems, subItemName, "", onClick: onClick);
subItem.Text = subText;
}
public static void BuildComboBox<T>(ComboBox comboBox, params T[] skip) where T : IConvertible
{
if (comboBox.Items.Count == 0)
+177 -118
View File
@@ -1,6 +1,7 @@
using ColorControl.Shared.Forms;
using NWin32;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;
namespace ColorControl.Shared.Native
@@ -8,7 +9,6 @@ namespace ColorControl.Shared.Native
// This class takes care of wrapping "Connecting and Configuring Displays(CCD) Win32 API"
public static class CCD
{
private static bool? IsHDRActive = null;
delegate (T, bool) DisplayConfigModeInfoDelegate<T>(DisplayConfigModeInfo modeInfo);
public enum DisplayTopology
@@ -62,66 +62,6 @@ namespace ColorControl.Shared.Native
}
}
public static void SetHDRState(bool enabled, string displayName = null)
{
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
if (err == NativeConstants.ERROR_SUCCESS)
{
var pathsArray = new DisplayConfigPathInfo[pathCount];
var modesArray = new DisplayConfigModeInfo[modeCount];
DisplayConfigTopologyId displayTopology;
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
if (err == NativeConstants.ERROR_SUCCESS)
{
foreach (var path in pathsArray)
{
if (!MatchDisplayNames(path, displayName))
{
continue;
}
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>();
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
for (int i = 0; i < modeCount; i++)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
{
setpacket.header.adapterId = modesArray[i].adapterId;
setpacket.header.id = modesArray[i].id;
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS && requestpacket.advancedColorSupported)
{
setpacket.enableAdvancedColor = enabled ? 1U : 0;
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
IsHDRActive = enabled;
}
}
}
}
}
}
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
}
public static LUID GetAdapterId(string displayName = null)
{
return ExecuteForModeConfig((modeInfo) =>
@@ -164,6 +104,13 @@ namespace ColorControl.Shared.Native
return true;
}
public static bool UninstallColorProfile(string profileName)
{
var name = Path.GetFileName(profileName);
return NativeMethods.UninstallColorProfileW(null, name, true);
}
private static bool MinRequiredVersion = true;
public static bool AddDisplayColorProfile(string displayName, string profileName, bool setAsDefault = true)
@@ -386,56 +333,22 @@ namespace ColorControl.Shared.Native
);
}
private static T ExecuteForModeConfig<T>(DisplayConfigModeInfoDelegate<T> infoDelegate, string displayName = null)
public static ColorParams GetColorParams(string displayName = null)
{
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
if (err == NativeConstants.ERROR_SUCCESS)
return ExecuteForModeConfig((modeInfo) =>
{
var pathsArray = new DisplayConfigPathInfo[pathCount];
var modesArray = new DisplayConfigModeInfo[modeCount];
DisplayConfigTopologyId displayTopology;
err = NativeMethods.QueryDisplayConfig(QueryDisplayFlags.DatabaseCurrent, ref pathCount, pathsArray, ref modeCount, modesArray, out displayTopology);
if (err == NativeConstants.ERROR_SUCCESS)
if (modeInfo.infoType == DisplayConfigModeInfoType.Source)
{
foreach (var path in pathsArray)
{
if (!MatchDisplayNames(path, displayName))
{
continue;
}
for (int i = 0; i < modeCount; i++)
{
var result = infoDelegate(modesArray[i]);
if (!result.Item2)
{
return result.Item1;
}
}
}
return (GetColorParamsForDisplayConfig(modeInfo), false);
}
}
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
return default;
return (new ColorParams(), true);
}, displayName
);
}
public static bool IsHDREnabled(string displayName = null)
private static T ExecuteForModeConfig<T>(DisplayConfigModeInfoDelegate<T> infoDelegate, string displayName = null)
{
if (IsHDRActive.HasValue)
{
return IsHDRActive.Value;
}
uint pathCount, modeCount;
var err = NativeMethods.GetDisplayConfigBufferSizes(QueryDisplayFlags.OnlyActivePaths, out pathCount, out modeCount);
@@ -454,30 +367,116 @@ namespace ColorControl.Shared.Native
continue;
}
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
var modes = modesArray.Where(m => m.id == path.sourceInfo.id || m.id == path.targetInfo.id);
for (int i = 0; i < modeCount; i++)
foreach (var mode in modes)
{
if (modesArray[i].infoType == DisplayConfigModeInfoType.Target)
var result = infoDelegate(mode);
if (!result.Item2)
{
requestpacket.header.adapterId = modesArray[i].adapterId;
requestpacket.header.id = modesArray[i].id;
err = NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket);
IsHDRActive = err == NativeConstants.ERROR_SUCCESS && requestpacket.advancedColorEnabled;
return IsHDRActive.Value;
return result.Item1;
}
}
}
}
}
return false;
if (err != NativeConstants.ERROR_SUCCESS)
{
throw new Win32Exception(err);
}
return default;
}
public static void SetHDRState(bool enabled, string displayName = null)
{
ExecuteForModeConfig((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Target)
{
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
requestpacket.header.adapterId = modeInfo.adapterId;
requestpacket.header.id = modeInfo.id;
if (NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket) == NativeConstants.ERROR_SUCCESS && requestpacket.advancedColorSupported)
{
var setpacket = new DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO();
setpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
setpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_SET_ADVANCED_COLOR_STATE;
setpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_SET_ADVANCED_COLOR_INFO>();
setpacket.header.adapterId = modeInfo.adapterId;
setpacket.header.id = modeInfo.id;
setpacket.enableAdvancedColor = enabled ? 1U : 0;
NativeMethods.DisplayConfigSetDeviceInfo(ref setpacket);
if (displayName != null)
{
_IsHDREnabledPerDisplay[displayName] = enabled;
}
_IsHDREnabledPerDisplay[""] = _IsHDREnabledPerDisplay.Any(kv => kv.Value);
}
return (false, false);
}
return (false, true);
}, displayName
);
}
public static (bool isSupported, bool isEnabled) GetAdvancedColorSupportedAndEnabled(string displayName = null)
{
return ExecuteForModeConfig((modeInfo) =>
{
if (modeInfo.infoType == DisplayConfigModeInfoType.Target)
{
var requestpacket = new DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
requestpacket.header.type = DISPLAYCONFIG_DEVICE_INFO_TYPE.DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO>();
requestpacket.header.adapterId = modeInfo.adapterId;
requestpacket.header.id = modeInfo.id;
var err = NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket);
var result = err == NativeConstants.ERROR_SUCCESS ? (requestpacket.advancedColorSupported, requestpacket.advancedColorEnabled) : (false, false);
return (result, false);
}
return ((false, false), true);
}, displayName
);
}
private static Dictionary<string, bool> _IsHDREnabledPerDisplay = new Dictionary<string, bool>();
public static bool IsHDREnabled(string displayName = null)
{
if (_IsHDREnabledPerDisplay.Any())
{
if (displayName == null)
{
return _IsHDREnabledPerDisplay.Any(kv => kv.Value);
}
if (_IsHDREnabledPerDisplay.TryGetValue(displayName, out var value))
{
return value;
}
}
var isEnabled = GetAdvancedColorSupportedAndEnabled(displayName).isEnabled;
_IsHDREnabledPerDisplay[displayName ?? ""] = isEnabled;
return isEnabled;
}
private static bool MatchDisplayNames(DisplayConfigPathInfo path, string displayName)
@@ -528,6 +527,30 @@ namespace ColorControl.Shared.Native
return 0;
}
private static ColorParams GetColorParamsForDisplayConfig(DisplayConfigModeInfo displayConfigModeInfo)
{
var requestpacket = new DISPLAYCONFIG_GET_DISPLAY_INFO();
requestpacket.header = new DISPLAYCONFIG_DEVICE_INFO_HEADER();
unchecked
{
requestpacket.header.type = (DISPLAYCONFIG_DEVICE_INFO_TYPE)DISPLAYCONFIG_DEVICE_INFO_GET_DISPLAY_INFO;
}
requestpacket.header.size = Marshal.SizeOf<DISPLAYCONFIG_GET_DISPLAY_INFO>();
requestpacket.header.adapterId = displayConfigModeInfo.adapterId;
requestpacket.header.id = displayConfigModeInfo.id;
var error = NativeMethods.DisplayConfigGetDeviceInfo(ref requestpacket);
if (error == NativeConstants.ERROR_SUCCESS)
{
return requestpacket.colorParams;
}
//throw new Win32Exception(error);
return new ColorParams();
}
private static bool EqualDisplayNames(string displayName1, string displayName2)
{
displayName1 = displayName1.Replace("\\", string.Empty);
@@ -559,6 +582,8 @@ namespace ColorControl.Shared.Native
DISPLAYCONFIG_DEVICE_INFO_SET_SDR_WHITE_LEVEL = 12,
}
private const uint DISPLAYCONFIG_DEVICE_INFO_GET_DISPLAY_INFO = 0xFFFFFFFE;
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_DEVICE_INFO_HEADER
{
@@ -880,6 +905,36 @@ namespace ColorControl.Shared.Native
DISPLAYCONFIG_COLOR_ENCODING_INTENSITY = 4,
}
[StructLayout(LayoutKind.Sequential)]
public struct ColorParams
{
// to get actual values: divide by 2^10 (precision equivalent to edid values)
// on newer w11, the divisor is 2^20 instead
public uint RedPointX;
public uint RedPointY;
public uint GreenPointX;
public uint GreenPointY;
public uint BluePointX;
public uint BluePointY;
public uint WhitePointX;
public uint WhitePointY;
// rest stored as nits * 10000
public uint MinLuminance;
public uint MaxLuminance;
public uint MaxFullFrameLuminance;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_DISPLAY_INFO
{
public DISPLAYCONFIG_DEVICE_INFO_HEADER header;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1964)]
public byte[] Stuff1;
public ColorParams colorParams;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)]
public byte[] Stuff2;
}
[StructLayout(LayoutKind.Sequential)]
private struct DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO
{
@@ -1046,9 +1101,13 @@ namespace ColorControl.Shared.Native
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_SDR_WHITE_LEVEL requestPacket);
[DllImport("user32")]
public static extern int DisplayConfigGetDeviceInfo(ref DISPLAYCONFIG_GET_DISPLAY_INFO requestPacket);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern bool InstallColorProfileW(string machineName, string profilename);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern bool UninstallColorProfileW(string machineName, string profilename, bool delete);
[DllImport("mscms", CharSet = CharSet.Unicode)]
public static extern int ColorProfileSetDisplayDefaultAssociation(WCS_PROFILE_MANAGEMENT_SCOPE scope, string profilename, COLORPROFILETYPE profiletype, COLORPROFILESUBTYPE profilesubtype, LUID targetadapterid, uint sourceid);
+87
View File
@@ -0,0 +1,87 @@
namespace ColorControl.Shared.Native;
public static class CmsFunctions
{
private const double PQ_M1 = 0.1593017578125;
private const double PQ_M2 = 78.84375;
private const double PQ_C1 = 0.8359375;
private const double PQ_C2 = 18.8515625;
private const double PQ_C3 = 18.6875;
public static double PqEotf(double signalValue)
{
var signalValuePow = Math.Pow(signalValue, 1D / PQ_M2);
var value1 = Math.Max(signalValuePow - PQ_C1, 0);
var value2 = PQ_C2 - PQ_C3 * signalValuePow;
var luminance = 10000D * Math.Pow(value1 / value2, 1D / PQ_M1);
return luminance;
}
public static double InvPqEotf(double luminance)
{
var normalizedLuminance = luminance / 10000D;
var luminancePow = Math.Pow(normalizedLuminance, PQ_M1);
var value1 = PQ_C1 + PQ_C2 * luminancePow;
var value2 = 1 + PQ_C3 * luminancePow;
var signalValue = Math.Pow(value1 / value2, PQ_M2);
return signalValue;
}
public static double SrgbInvEotf(double luminance, double whiteLuminance, double blackLuminance, double gamma = 2.4)
{
//const double X1 = 0.0404482362771082;
const double X2 = 0.00313066844250063;
var x = (luminance - blackLuminance) / (whiteLuminance - blackLuminance);
if (x > 1)
{
x = 1;
}
else if (x < 0)
{
x = 0;
}
else if (x <= X2)
{
x *= 12.92;
}
else
{
x = 1.055 * Math.Pow(x, 1 / gamma) - 0.055;
}
return x;
}
public static (double, double) SrgbAcm(int signalValue, double whiteLuminance, double blackLuminance = 0, double gamma = 2.2)
{
var b = signalValue / 1023;
var c = PqEotf(b);
var d = SrgbInvEotf(c, whiteLuminance, blackLuminance);
var e = blackLuminance + (whiteLuminance - blackLuminance) * Math.Pow(d, gamma);
var f = InvPqEotf(Math.Max(0, e));
var x = f + Math.Min(1, c / whiteLuminance) * (b - f);
return (b, x);
}
public static double GammaRgb(double signalValue, double gamma = 2.4)
{
return signalValue <= 0.0031308 ? signalValue * 12.92 : 1.055 * Math.Pow(signalValue, 1.0 / gamma) - 0.055;
}
public static double RgbToLinear(double signalValue, double gamma = 2.4)
{
return signalValue <= 0.04045 ? signalValue / 12.92 : Math.Pow((signalValue + 0.055) / 1.055, gamma);
}
}
+13
View File
@@ -1,4 +1,5 @@
using ColorControl.Shared.Contracts;
using ColorControl.Shared.Native;
using Microsoft.Win32;
using Microsoft.Win32.TaskScheduler;
using NWin32;
@@ -336,6 +337,18 @@ namespace ColorControl.Shared.Services
}
}
public bool UninstallColorProfile(string filename)
{
if (!_winApiService.IsAdministrator())
{
return _rpcService.Call<bool>(nameof(UninstallColorProfile), filename);
}
var deleteResult = CCD.UninstallColorProfile(filename);
return deleteResult;
}
private static bool UpdateShortcut(string path, string arguments, bool removeArguments = false)
{
if (File.Exists(path))
+52
View File
@@ -0,0 +1,52 @@
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Runtime.CompilerServices;
namespace ColorControl.Shared.XForms;
public abstract class BaseViewModel : IDataErrorInfo, INotifyPropertyChanged
{
public virtual string Error => null;
public bool IsValid { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
public virtual string this[string columnName]
{
get
{
var validationResults = new List<ValidationResult>();
var fieldValid = Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this)
, new ValidationContext(this)
{
MemberName = columnName
}
, validationResults);
bool isValid;
if (fieldValid)
{
var allValidationResults = new List<ValidationResult>();
isValid = PreValidate() && Validator.TryValidateObject(this, new ValidationContext(this), allValidationResults, true);
}
else
{
isValid = false;
}
if (isValid != IsValid)
{
IsValid = isValid;
OnPropertyChanged("IsValid");
}
return fieldValid ? null : validationResults.First().ErrorMessage;
}
}
public virtual bool PreValidate()
{
return true;
}
}
+4
View File
@@ -271,6 +271,10 @@ namespace novideo_srgb
}
catch
{
if (display == null)
{
throw new InvalidOperationException("No display");
}
return new EDID(display.Output.PhysicalGPU.ReadEDIDData(display.Output));
}
}