我在前面几章里面提到过ColorMatrix,可以将图像的色彩进行仿射变换。但是如果要对图像的色彩进行非线性变换的话,那就必须用到更强悍的API了。在Windows早期,有一套标准的色彩管理的API,叫做ICM 2.0 (Image Color Management 2.0)。在Windows Vista 以后,这套API升级成了WCS 1.0 (Windows Color System 1.0)。 这套API实现了www.color.org 上说的色彩管理的算法,具体的内容在http://msdn.microsoft.com/en-us/library/dd372446(VS.85).aspx 。其中包括了显示,设备以及Gamut映射的算法。
刚才顺手抄了一个使用ICM 2.0转换图像的算法,用C#把ICM的几个API 封装了一下,这样就可以使用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
{
publicclassICM
{
#region Consts
constuint PROFILE_FILENAME = 1; // profile data is NULLterminated filename
constuint PROFILE_READ = 1; // opened for read access
constuint FILE_SHARE_READ = 0x00000001;
constuint OPEN_EXISTING = 3;
constuint PROOF_MODE = 0x00000001;
constuint NORMAL_MODE = 0x00000002;
constuint BEST_MODE = 0x00000003;
constuint ENABLE_GAMUT_CHECKING = 0x00010000;
constuint USE_RELATIVE_COLORIMETRIC = 0x00020000;
constuint FAST_TRANSLATE = 0x00040000;
constint LCS_SIGNATURE = 0x50534F43; /* PSOC */
#endregion
#region Types
publicenumBMFORMAT
{
//
// 16bpp - 5 bits per channel. The mostsignificant bit is ignored.
//
BM_x555RGB = 0x0000,
BM_x555XYZ = 0x0101,
BM_x555Yxy,
BM_x555Lab,
BM_x555G3CH,
//
// Packed 8 bits per channel => 8bppfor GRAY and
// 24bpp for the 3 channel colors, morefor hifi channels
//
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,
//
// 32bpp - 8 bits per channel. The mostsignificant byte is ignored
// for the 3 channel colors.
//
BM_xRGBQUADS = 0x0008,
BM_xBGRQUADS = 0x0010,
BM_xG3CHQUADS = 0x0304,
BM_KYMCQUADS,
BM_CMYKQUADS = 0x0020,
//
// 32bpp - 10 bits per channel. The 2most significant bits are ignored.
//
BM_10b_RGB = 0x0009,
BM_10b_XYZ = 0x0401,
BM_10b_Yxy,
BM_10b_Lab,
BM_10b_G3CH,
//
// 32bpp - named color indices (1-based)
//
BM_NAMED_INDEX,
//
// Packed 16 bits per channel => 16bppfor GRAY and
// 48bpp for the 3 channel colors.
//
BM_16b_RGB = 0x000A,
BM_16b_XYZ = 0x0501,
BM_16b_Yxy,
BM_16b_Lab,
BM_16b_G3CH,
BM_16b_GRAY,
//
// 16 bpp - 5 bits for Red & Blue, 6bits for Green
//
BM_565RGB = 0x0001,
//#if NTDDI_VERSION >= NTDDI_LONGHORN
//
// scRGB - 32 bits per channel floatingpoint
//16 bits per channel floating point
//
BM_32b_scRGB = 0x0601,
BM_32b_scARGB = 0x0602,
BM_S2DOT13FIXED_scRGB = 0x0603,
BM_S2DOT13FIXED_scARGB = 0x0604
//#endif // NTDDI_VERSION >=NTDDI_LONGHORN
}
[StructLayout(LayoutKind.Sequential)]
publicstructCIEXYZ
{
publicint ciexyzX, ciexyzY, ciexyzZ;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
publicstructtagPROFILE
{
publicuint dwType;
publicstring pProfileData;
publicuint cbDataSize;
}
[StructLayout(LayoutKind.Sequential)]
publicstructCIEXYZTRIPLE
{
publicCIEXYZ ciexyzRed, ciexyzGreen, ciexyBlue;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
structLOGCOLORSPACE
{
publicuint Signature, Version, Size;
publicint CSType, Intent, GammaRed, GammaGreen,GammaBlue;
publicCIEXYZTRIPLE Endpoints;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
publicstring Filename;
}
publicenumGamutMappingIntent
{
LCS_GM_ABS_COLORIMETRIC =0x00000008,
LCS_GM_BUSINESS = 0x00000001,
LCS_GM_GRAPHICS = 0x00000002,
LCS_GM_IMAGES = 0x00000004
}
publicenumLogicalColorSpace
{
LCS_CALIBRATED_RGB = 0x00000000,
LCS_sRGB = 0x73524742,
LCS_WINDOWS_COLOR_SPACE =0x57696E20
}
#endregion
publicdelegateboolICMProgressProcCallback(uint ulMax, uint ulCurrent, int ulCallbackData);
[DllImport("mscms.dll", SetLastError = true)]
staticexternIntPtr OpenColorProfile(reftagPROFILE pProfile, uint AccessMode, uint ShareMode, uint CreateMode);
[DllImport("mscms.dll", SetLastError = true)]
staticexternbool 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)]
staticexternbool CloseColorProfile(IntPtr profile);
[DllImport("mscms.dll", SetLastError = true)]
staticexternbool DeleteColorTransform(IntPtr transform);
[DllImport("mscms.dll", SetLastError = true)]
staticexternIntPtr CreateColorTransform(refLOGCOLORSPACE pLogColorSpace, IntPtr hDestProfile, IntPtr hTargetProfile, uint dwFlags);
publicvoid Convert(string profilePath, string imageFilePath, string outputPath)
{
LOGCOLORSPACE logColorSpace = newLOGCOLORSPACE();
logColorSpace.Signature =LCS_SIGNATURE; /* LCS_SIGNATURE */
logColorSpace.Intent = (int)GamutMappingIntent.LCS_GM_IMAGES; /* LCS_GM_IMAGES */
logColorSpace.Version = 0x0400;
logColorSpace.Size = (uint)Marshal.SizeOf(logColorSpace);
logColorSpace.CSType = (int)LogicalColorSpace.LCS_sRGB; /* LCS_sRGB */
IntPtr Destprofile;
tagPROFILE profile = newtagPROFILE();
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 = newFileStream(imageFilePath, FileMode.Open, FileAccess.Read);
Bitmap bmpTemp = (Bitmap)Image.FromStream(fs, false, false);
Bitmap bmp = newBitmap(bmpTemp);
fs.Close();
bmpTemp.Dispose();
BitmapData bmData = bmp.LockBits(newRectangle(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(Destprofile);
}
else
{
int errorCode = Marshal.GetLastWin32Error();
thrownewCOMException("Error", errorCode);
}
}
}
}