好久没有写了,真是灰常地惭愧。Heroes都已经放到第24集了,而我只写到第11集,实在是很惭愧。
我在前面几章里面提到过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来转换不同的图像了。
1
using
System;
2
using
System.Collections.Generic;
3
using
System.Text;
4
using
System.Runtime.InteropServices;
5
using
System.Drawing;
6
using
System.Drawing.Imaging;
7
using
System.IO;
8
9
namespace
ICCConverter
10
{
11
public
class
ICM
12
{
13
#region
Consts
14
15
const
uint
PROFILE_FILENAME
=
1
;
//
profile data is NULL terminated filename
16
const
uint
PROFILE_READ
=
1
;
//
opened for read access
17
const
uint
FILE_SHARE_READ
=
0x00000001
;
18
const
uint
OPEN_EXISTING
=
3
;
19
const
uint
PROOF_MODE
=
0x00000001
;
20
const
uint
NORMAL_MODE
=
0x00000002
;
21
const
uint
BEST_MODE
=
0x00000003
;
22
const
uint
ENABLE_GAMUT_CHECKING
=
0x00010000
;
23
const
uint
USE_RELATIVE_COLORIMETRIC
=
0x00020000
;
24
const
uint
FAST_TRANSLATE
=
0x00040000
;
25
const
int
LCS_SIGNATURE
=
0x50534F43
;
/*
PSOC
*/
26
27
#endregion
28
29
#region
Types
30
31
public
enum
BMFORMAT
32
{
33
//
34
//
16bpp - 5 bits per channel. The most significant bit is ignored.
35
//
36
37
BM_x555RGB
=
0x0000
,
38
BM_x555XYZ
=
0x0101
,
39
BM_x555Yxy,
40
BM_x555Lab,
41
BM_x555G3CH,
42
43
//
44
//
Packed 8 bits per channel => 8bpp for GRAY and
45
//
24bpp for the 3 channel colors, more for hifi channels
46
//
47
48
BM_RGBTRIPLETS
=
0x0002
,
49
BM_BGRTRIPLETS
=
0x0004
,
50
BM_XYZTRIPLETS
=
0x0201
,
51
BM_YxyTRIPLETS,
52
BM_LabTRIPLETS,
53
BM_G3CHTRIPLETS,
54
BM_5CHANNEL,
55
BM_6CHANNEL,
56
BM_7CHANNEL,
57
BM_8CHANNEL,
58
BM_GRAY,
59
60
//
61
//
32bpp - 8 bits per channel. The most significant byte is ignored
62
//
for the 3 channel colors.
63
//
64
65
BM_xRGBQUADS
=
0x0008
,
66
BM_xBGRQUADS
=
0x0010
,
67
BM_xG3CHQUADS
=
0x0304
,
68
BM_KYMCQUADS,
69
BM_CMYKQUADS
=
0x0020
,
70
71
//
72
//
32bpp - 10 bits per channel. The 2 most significant bits are ignored.
73
//
74
75
BM_10b_RGB
=
0x0009
,
76
BM_10b_XYZ
=
0x0401
,
77
BM_10b_Yxy,
78
BM_10b_Lab,
79
BM_10b_G3CH,
80
81
//
82
//
32bpp - named color indices (1-based)
83
//
84
85
BM_NAMED_INDEX,
86
87
//
88
//
Packed 16 bits per channel => 16bpp for GRAY and
89
//
48bpp for the 3 channel colors.
90
//
91
92
BM_16b_RGB
=
0x000A
,
93
BM_16b_XYZ
=
0x0501
,
94
BM_16b_Yxy,
95
BM_16b_Lab,
96
BM_16b_G3CH,
97
BM_16b_GRAY,
98
99
//
100
//
16 bpp - 5 bits for Red & Blue, 6 bits for Green
101
//
102
103
BM_565RGB
=
0x0001
,
104
105
//
#if NTDDI_VERSION >= NTDDI_LONGHORN
106
//
107
//
scRGB - 32 bits per channel floating point
108
//
16 bits per channel floating point
109
//
110
111
BM_32b_scRGB
=
0x0601
,
112
BM_32b_scARGB
=
0x0602
,
113
BM_S2DOT13FIXED_scRGB
=
0x0603
,
114
BM_S2DOT13FIXED_scARGB
=
0x0604
115
//
#endif
//
NTDDI_VERSION >= NTDDI_LONGHORN
116
117
}
118
119
[StructLayout(LayoutKind.Sequential)]
120
public
struct
CIEXYZ
121
{
122
public
int
ciexyzX, ciexyzY, ciexyzZ;
123
}
124
125
[StructLayout(LayoutKind.Sequential, CharSet
=
CharSet.Ansi)]
126
public
struct
tagPROFILE
127
{
128
public
uint
dwType;
129
public
string
pProfileData;
130
public
uint
cbDataSize;
131
}
132
133
[StructLayout(LayoutKind.Sequential)]
134
public
struct
CIEXYZTRIPLE
135
{
136
public
CIEXYZ ciexyzRed, ciexyzGreen, ciexyBlue;
137
}
138
139
[StructLayout(LayoutKind.Sequential, CharSet
=
CharSet.Unicode)]
140
struct
LOGCOLORSPACE
141
{
142
public
uint
Signature, Version, Size;
143
public
int
CSType, Intent, GammaRed, GammaGreen, GammaBlue;
144
public
CIEXYZTRIPLE Endpoints;
145
146
[MarshalAs(UnmanagedType.ByValTStr, SizeConst
=
260
)]
147
public
string
Filename;
148
}
149
150
public
enum
GamutMappingIntent
151
{
152
LCS_GM_ABS_COLORIMETRIC
=
0x00000008
,
153
LCS_GM_BUSINESS
=
0x00000001
,
154
LCS_GM_GRAPHICS
=
0x00000002
,
155
LCS_GM_IMAGES
=
0x00000004
156
}
157
158
public
enum
LogicalColorSpace
159
{
160
LCS_CALIBRATED_RGB
=
0x00000000
,
161
LCS_sRGB
=
0x73524742
,
162
LCS_WINDOWS_COLOR_SPACE
=
0x57696E20
163
}
164
165
166
167
#endregion
168
169
public
delegate
bool
ICMProgressProcCallback(
uint
ulMax,
uint
ulCurrent,
int
ulCallbackData);
170
171
[DllImport(
"
mscms.dll
"
, SetLastError
=
true
)]
172
static
extern
IntPtr OpenColorProfile(
ref
tagPROFILE pProfile,
uint
AccessMode,
uint
ShareMode,
uint
CreateMode);
173
174
175
176
[DllImport(
"
mscms.dll
"
, SetLastError
=
true
)]
177
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);
178
179
[DllImport(
"
mscms.dll
"
, SetLastError
=
true
)]
180
static
extern
bool
CloseColorProfile(IntPtr profile);
181
182
[DllImport(
"
mscms.dll
"
, SetLastError
=
true
)]
183
static
extern
bool
DeleteColorTransform(IntPtr transform);
184
185
[DllImport(
"
mscms.dll
"
, SetLastError
=
true
)]
186
static
extern
IntPtr CreateColorTransform(
ref
LOGCOLORSPACE pLogColorSpace, IntPtr hDestProfile, IntPtr hTargetProfile,
uint
dwFlags);
187
188
public
void
Convert(
string
profilePath,
string
imageFilePath,
string
outputPath)
189
{
190
191
LOGCOLORSPACE logColorSpace
=
new
LOGCOLORSPACE();
192
193
logColorSpace.Signature
=
LCS_SIGNATURE;
/*
LCS_SIGNATURE
*/
194
logColorSpace.Intent
=
(
int
)GamutMappingIntent.LCS_GM_IMAGES;
/*
LCS_GM_IMAGES
*/
195
logColorSpace.Version
=
0x0400
;
196
logColorSpace.Size
=
(
uint
)Marshal.SizeOf(logColorSpace);
197
logColorSpace.CSType
=
(
int
)LogicalColorSpace.LCS_sRGB;
/*
LCS_sRGB
*/
198
IntPtr Destprofile;
199
200
tagPROFILE profile
=
new
tagPROFILE();
201
profile.dwType
=
PROFILE_FILENAME;
202
profile.pProfileData
=
profilePath;
203
profile.cbDataSize
=
(
uint
)profile.pProfileData.Length
+
1
;
204
Destprofile
=
OpenColorProfile(
ref
profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING);
205
IntPtr pTransforms
=
CreateColorTransform(
ref
logColorSpace, Destprofile, IntPtr.Zero, BEST_MODE);
206
207
if
(pTransforms
!=
IntPtr.Zero)
208
{
209
FileStream fs
=
new
FileStream(imageFilePath, FileMode.Open, FileAccess.Read);
210
Bitmap bmpTemp
=
(Bitmap)Image.FromStream(fs,
false
,
false
);
211
Bitmap bmp
=
new
Bitmap(bmpTemp);
212
fs.Close();
213
bmpTemp.Dispose();
214
215
BitmapData bmData
=
bmp.LockBits(
new
Rectangle(
0
,
0
, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
216
bool
success
=
TranslateBitmapBits(
217
pTransforms,
218
bmData.Scan0,
219
BMFORMAT.BM_RGBTRIPLETS,
220
(
uint
)bmData.Width,
221
(
uint
)bmData.Height,
222
(
uint
)bmData.Stride,
223
bmData.Scan0,
224
BMFORMAT.BM_RGBTRIPLETS,
225
(
uint
)bmData.Stride,
null
,
0
);
226
227
bmp.UnlockBits(bmData);
228
bmp.Save(outputPath, ImageFormat.Jpeg);
229
CloseColorProfile(Destprofile);
230
DeleteColorTransform(Destprofile);
231
}
232
else
233
{
234
int
errorCode
=
Marshal.GetLastWin32Error();
235
throw
new
COMException(
"
Error
"
, errorCode);
236
}
237
}
238
}
239
}
240
241
这一章其实跟GDI+并没有什么太大的关系,不知道什么时候这些代码会直接放在.NET Framework Code里面,这样用起来就方便了。