ColorMatrix 僅支援仿射(線性)色彩變換。若需非線性轉換(如裝置色域對應、色調映射),須使用系統級色彩管理 API。Windows 早期提供 ICM 2.0 (Image Color Management),Vista 後升級為 WCS 1.0 (Windows Color System),實作 ICC 標準演算法,涵蓋顯示、裝置、色域映射等。詳細文件見 MSDN。
以下為完整封裝代碼,可直接呼叫 ICM.Convert(profilePath, imageFilePath, outputPath) 套用 ICC 設定檔轉換影像:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
namespace ICCConverter
{
public class ICM
{
#region Constants
const uint PROFILE_FILENAME = 1;
const uint PROFILE_READ = 1;
const uint FILE_SHARE_READ = 0x00000001;
const uint OPEN_EXISTING = 3;
const uint PROOF_MODE = 0x00000001;
const uint NORMAL_MODE = 0x00000002;
const uint BEST_MODE = 0x00000003;
const uint ENABLE_GAMUT_CHECKING = 0x00010000;
const uint USE_RELATIVE_COLORIMETRIC = 0x00020000;
const uint FAST_TRANSLATE = 0x00040000;
const int LCS_SIGNATURE = 0x50534F43; // "PSOC"
#endregion
#region Types
public enum BMFORMAT
{
BM_x555RGB = 0x0000, BM_x555XYZ = 0x0101, BM_x555Yxy, BM_x555Lab, BM_x555G3CH,
BM_RGBTRIPLETS = 0x0002, BM_BGRTRIPLETS = 0x0004, BM_XYZTRIPLETS = 0x0201, BM_YxyTRIPLETS,
BM_LabTRIPLETS, BM_G3CHTRIPLETS, BM_5CHANNEL, BM_6CHANNEL, BM_7CHANNEL, BM_8CHANNEL,
BM_GRAY, BM_xRGBQUADS = 0x0008, BM_xBGRQUADS = 0x0010, BM_xG3CHQUADS = 0x0304,
BM_KYMCQUADS, BM_CMYKQUADS = 0x0020, BM_10b_RGB = 0x0009, BM_10b_XYZ = 0x0401,
BM_10b_Yxy, BM_10b_Lab, BM_10b_G3CH, BM_NAMED_INDEX,
BM_16b_RGB = 0x000A, BM_16b_XYZ = 0x0501, BM_16b_Yxy, BM_16b_Lab, BM_16b_G3CH,
BM_16b_GRAY, BM_565RGB = 0x0001,
BM_32b_scRGB = 0x0601, BM_32b_scARGB = 0x0602, BM_S2DOT13FIXED_scRGB = 0x0603,
BM_S2DOT13FIXED_scARGB = 0x0604
}
[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZ { public int ciexyzX, ciexyzY, ciexyzZ; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct tagPROFILE { public uint dwType; public string pProfileData; public uint cbDataSize; }
[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZTRIPLE { public CIEXYZ ciexyzRed, ciexyzGreen, ciexyBlue; }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct LOGCOLORSPACE
{
public uint Signature, Version, Size;
public int CSType, Intent, GammaRed, GammaGreen, GammaBlue;
public CIEXYZTRIPLE Endpoints;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string Filename;
}
public enum GamutMappingIntent
{
LCS_GM_ABS_COLORIMETRIC = 0x00000008,
LCS_GM_BUSINESS = 0x00000001,
LCS_GM_GRAPHICS = 0x00000002,
LCS_GM_IMAGES = 0x00000004
}
public enum LogicalColorSpace
{
LCS_CALIBRATED_RGB = 0x00000000,
LCS_sRGB = 0x73524742,
LCS_WINDOWS_COLOR_SPACE = 0x57696E20
}
#endregion
public delegate bool ICMProgressProcCallback(uint ulMax, uint ulCurrent, int ulCallbackData);
[DllImport("mscms.dll", SetLastError = true)]
static extern IntPtr OpenColorProfile(ref tagPROFILE pProfile, uint AccessMode, uint ShareMode, uint CreateMode);
[DllImport("mscms.dll", SetLastError = true)]
static extern bool TranslateBitmapBits(IntPtr pTransform, IntPtr inBuffer, BMFORMAT inFormat, uint width, uint height, uint stride, IntPtr outBuffer, BMFORMAT outFormat, uint outStride, ICMProgressProcCallback pfCallback, int CallBackParam);
[DllImport("mscms.dll", SetLastError = true)]
static extern bool CloseColorProfile(IntPtr profile);
[DllImport("mscms.dll", SetLastError = true)]
static extern bool DeleteColorTransform(IntPtr transform);
[DllImport("mscms.dll", SetLastError = true)]
static extern IntPtr CreateColorTransform(ref LOGCOLORSPACE pLogColorSpace, IntPtr hDestProfile, IntPtr hTargetProfile, uint dwFlags);
public void Convert(string profilePath, string imageFilePath, string outputPath)
{
LOGCOLORSPACE logColorSpace = new LOGCOLORSPACE();
logColorSpace.Signature = LCS_SIGNATURE;
logColorSpace.Intent = (int)GamutMappingIntent.LCS_GM_IMAGES;
logColorSpace.Version = 0x0400;
logColorSpace.Size = (uint)Marshal.SizeOf(logColorSpace);
logColorSpace.CSType = (int)LogicalColorSpace.LCS_sRGB;
IntPtr destProfile;
tagPROFILE profile = new tagPROFILE();
profile.dwType = PROFILE_FILENAME;
profile.pProfileData = profilePath;
profile.cbDataSize = (uint)profile.pProfileData.Length + 1;
destProfile = OpenColorProfile(ref profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING);
IntPtr pTransforms = CreateColorTransform(ref logColorSpace, destProfile, IntPtr.Zero, BEST_MODE);
if (pTransforms != IntPtr.Zero)
{
FileStream fs = new FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
Bitmap bmpTemp = (Bitmap)Image.FromStream(fs, false, false);
Bitmap bmp = new Bitmap(bmpTemp);
fs.Close();
bmpTemp.Dispose();
BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
bool success = TranslateBitmapBits(
pTransforms,
bmData.Scan0,
BMFORMAT.BM_RGBTRIPLETS,
(uint)bmData.Width,
(uint)bmData.Height,
(uint)bmData.Stride,
bmData.Scan0,
BMFORMAT.BM_RGBTRIPLETS,
(uint)bmData.Stride, null, 0);
bmp.UnlockBits(bmData);
bmp.Save(outputPath, ImageFormat.Jpeg);
CloseColorProfile(destProfile);
DeleteColorTransform(pTransforms);
}
else
{
int errorCode = Marshal.GetLastWin32Error();
throw new System.Runtime.InteropServices.COMException("Error", errorCode);
}
}
}
}
此功能屬系統級色彩管理,尚未內建於 .NET Framework,期待未來版本原生支援以簡化開發。