
需求:
很久很久以前,还在做asp的时候,在我网站的客户都会自动创建一个2级名的网站,设置了相应的模块。但有些客户喜欢某个模块的样式,不喜欢它的颜色,便要求我们改成他所喜欢的颜色。修改其实也很容易,无法就是把图片DOWN下来利用PS修改一下色调。但是,客户数量的庞大使得我们无暇一一满足。所以便想到客户可以自己通过web修改为自己喜欢的颜色。这样,普通的CSS很方便,但是模块中的图片颜色却不能修改。现在做.net了,又想这样做,折腾了好几天,终于实现了。
原理:
其实,修改简单修改图片的颜色,就是修改图片的色调(色相)。做过ps的人都知道,简单的拖动色掉的滚动条,图片就变为不同的颜色。而改变图片的色调,其实质也就是改变图片的R、G、B值,大家都知道无论任何色彩都是由RGB的不同比例组合而得到的。所以通过改变RGB然后重新组合就达到了改变图片颜色的效果,但是,除非专业的色彩搭配的人员,一般不可能通过单独一一调整R/G/B来达到自己想要的色彩效果,所以,就出现了色调这个名词,通过一定的计算公式,把色调的调整值换算为R/G/B的值,然后修改R/G/B来达到预期的效果。同样,修改图片的饱和度、明暗也是同样的原理(下两个专题可以陆续详细介绍)。
具体代码:
代码的关键,就是把色调值换算为R/G/B对应的值。
1、页面HTML
1
<%
@ Page language="c#" Codebehind="PS.aspx.cs" AutoEventWireup="false" Inherits="BxgTest.PS" %>
2
<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
3
< HTML >
4
< HEAD >
5
< title > PS </ title >
6
< meta content ="Microsoft Visual Studio .NET 7.1" name ="GENERATOR" >
7
< meta content ="C#" name ="CODE_LANGUAGE" >
8
< meta content ="JavaScript" name ="vs_defaultClientScript" >
9
< meta content ="http://schemas.microsoft.com/intellisense/ie5" name ="vs_targetSchema" >
10
< SCRIPT language ="JavaScript" >
11
12
var changedNotApplied = false;
13
var hueText = "";
14
15
function hueClick()
16
{
17
hue = event.offsetX;
18
//changedNotApplied = true;
19
20
document.Form1.TextBox1.value=hue;
21
return false;
22
}
23
</ SCRIPT >
24
</ HEAD >
25
< body MS_POSITIONING ="GridLayout" >
26
< form id ="Form1" method ="post" runat ="server" >
27
< TABLE id ="Table1" style ="WIDTH: 912px; HEIGHT: 158px" cellSpacing ="0" cellPadding ="0"
28
width ="912" border ="1" >
29
< TR >
30
< TD style ="WIDTH: 139px" >< FONT face ="宋体" > 原始图象 </ FONT ></ TD >
31
< TD >< asp:image id ="Image1" runat ="server" ></ asp:image ></ TD >
32
</ TR >
33
< TR >
34
< TD style ="WIDTH: 139px" >< FONT face ="宋体" > 修改后的图象 </ FONT ></ TD >
35
< TD >< FONT face ="宋体" >< asp:image id ="Image2" runat ="server" ></ asp:image ></ FONT ></ TD >
36
</ TR >
37
< TR >
38
< TD style ="WIDTH: 139px" >< FONT face ="宋体" > 设置值 </ FONT ></ TD >
39
< TD >< IMG alt ="simp" src ="imgs.png" style ="CURSOR: hand" onclick ="hueClick()" name ="huebar" >
40
< asp:TextBox id ="TextBox1" runat ="server" Width ="48px" ></ asp:TextBox >< FONT face ="宋体" > 0~360 </ FONT ></ TD >
41
</ TR >
42
< TR >
43
< TD style ="WIDTH: 139px" >
44
< asp:Button id ="Button1" runat ="server" Text ="Button" ></ asp:Button ></ TD >
45
< TD ></ TD >
46
</ TR >
47
</ TABLE >
48
</ form >
49
</ body >
50
</ HTML >
51
其中有段JS,实质就是把选择的颜色转变为色掉,这个很简单。你预先弄张七彩图,长度为360(色调范围)然后取X位置就是对应的值了。 


2

3

4

5

6

7

8

9

10



11

12

13

14

15

16



17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

二、***.aspx.cs
*.aspx.cs
1using System;
2using System.Collections;
3using System.ComponentModel;
4using System.Data;
5using System.Drawing;
6using System.Web;
7using System.Web.SessionState;
8using System.Web.UI;
9using System.Web.UI.WebControls;
10using System.Web.UI.HtmlControls;
11
12namespace BxgTest
13{
14 /** <summary>
15 /// PS 的摘要说明。
16 /// </summary>
17 public class PS : System.Web.UI.Page
18 {
19 protected System.Web.UI.WebControls.Image Image1;
20 protected System.Web.UI.WebControls.Image Image2;
21 protected System.Web.UI.WebControls.Button Button1;
22 protected System.Web.UI.WebControls.TextBox TextBox1;
23 protected System.Web.UI.HtmlControls.HtmlForm Form1;
24
25 private void Page_Load(object sender, System.EventArgs e)
26 {
27 if(!Page.IsPostBack)
28 bindPIC();
29 }
30
31 private void bindPIC()
32 {
33 Image1.ImageUrl = Server.MapPath("112.jpg");
34 Image2.ImageUrl = Server.MapPath("112P.jpg");
35 }
36
37 Web 窗体设计器生成的代码#region Web 窗体设计器生成的代码
38 override protected void OnInit(EventArgs e)
39 {
40 //
41 // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
42 //
43 InitializeComponent();
44 base.OnInit(e);
45 }
46
47 /** <summary>
48 /// 设计器支持所需的方法 - 不要使用代码编辑器修改
49 /// 此方法的内容。
50 /// </summary>
51 private void InitializeComponent()
52 {
53 this.Button1.Click += new System.EventHandler(this.Button1_Click);
54 this.Load += new System.EventHandler(this.Page_Load);
55
56 }
57 #endregion
58
59 private void Button1_Click(object sender, System.EventArgs e)
60 {
61 System.Drawing.Image image = System.Drawing.Image.FromFile(System.Web.HttpContext.Current.Server.MapPath("112.jpg"));
62 //取高和宽
63 int phWidth = image.Width;
64 int phHeight =image.Height;
65 //建新图,指定格式为每像素 24 位;红色、绿色和蓝色分量各使用 8 位。
66 System.Drawing.Bitmap bmPhoto = new System.Drawing.Bitmap(phWidth, phHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
67 //设置分辨率
68 bmPhoto.SetResolution(image.HorizontalResolution,image.VerticalResolution);
69 //准备Graphics
70 System.Drawing.Graphics grPhoto = System.Drawing.Graphics.FromImage(bmPhoto);
71
72
73 //指定消除锯齿的呈现。
74 grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
75 grPhoto.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
76 grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
77 grPhoto.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
78
79 //拷贝原图到做图区
80 grPhoto.DrawImage(
81 image,
82 new System.Drawing.Rectangle(0, 0, phWidth, phHeight),
83 0,
84 0,
85 phWidth,
86 phHeight,
87 System.Drawing.GraphicsUnit.Pixel);
88
89
90 HueModifier h = new HueModifier();
91 h.Hue = Convert.ToInt32(TextBox1.Text);
92
93 Bitmap imgnew = h.Apply(bmPhoto);
94
95 imgnew.Save(Server.MapPath("112P.jpg"), System.Drawing.Imaging.ImageFormat.Jpeg);
96
97 imgnew.Dispose();
98 grPhoto.Dispose();
99 bmPhoto.Dispose();
100 image.Dispose();
101 }
102 }
103}
这里需要注意的是,必须是每象素24位的图片,所以,我先新建了和原来图片一样大的图,然后复制过去。
3、处理类(部分代码来自网络)
CLASS
1using System;
2using System.Drawing;
3using System.Drawing.Imaging;
4
5namespace BxgTest
6{
7 public class RGB
8 {
9 public const short R = 2;
10 public const short G = 1;
11 public const short B = 0;
12
13 public byte Red;
14 public byte Green;
15 public byte Blue;
16
17 public System.Drawing.Color Color
18 {
19 get { return System.Drawing.Color.FromArgb(Red, Green, Blue); }
20 set
21 {
22 Red = value.R;
23 Green = value.G;
24 Blue = value.B;
25 }
26 }
27
28 public RGB(){}
29
30 public RGB(byte red, byte green, byte blue)
31 {
32 this.Red = red;
33 this.Green = green;
34 this.Blue = blue;
35 }
36 };
37
38
39
40
41
42
43 public class HSL
44 {
45 public int Hue; // 0-359 : 色调
46 public double Saturation; // 0-1 : 饱和度
47 public double Luminance; // 0-1 : 明暗度
48
49 public HSL(){}
50
51 public HSL(int hue, double saturation, double luminance)
52 {
53 this.Hue = hue;
54 this.Saturation = saturation;
55 this.Luminance = luminance;
56 }
57 };
58
59
60
61
62
63
64
65 public class ColorConverter
66 {
67 public static void RGB2HSL(RGB rgb, HSL hsl)
68 {
69 double r = (rgb.Red / 255.0);
70 double g = (rgb.Green / 255.0);
71 double b = (rgb.Blue / 255.0);
72
73 double min = Math.Min(Math.Min(r, g), b);
74 double max = Math.Max(Math.Max(r, g), b);
75 double delta = max - min;
76
77 hsl.Luminance = (max + min) / 2;
78
79 if (delta == 0)
80 {
81 hsl.Hue = 0;
82 hsl.Saturation = 0.0;
83 }
84 else
85 {
86 hsl.Saturation = (hsl.Luminance < 0.5) ? (delta / (max + min)) : (delta / (2 - max - min));
87
88 double del_r = (((max - r) / 6) + (delta / 2)) / delta;
89 double del_g = (((max - g) / 6) + (delta / 2)) / delta;
90 double del_b = (((max - b) / 6) + (delta / 2)) / delta;
91 double hue;
92
93 if (r == max)
94 hue = del_b - del_g;
95 else if (g == max)
96 hue = (1.0 / 3) + del_r - del_b;
97 else
98 hue = (2.0 / 3) + del_g - del_r;
99
100 if (hue < 0)
101 hue += 1;
102 if (hue > 1)
103 hue -= 1;
104
105 hsl.Hue = (int) (hue * 360);
106 }
107 }
108
109 /** <summary>
110 /// 把HSH转成对应的RGB
111 /// </summary>
112 /// <param name="hsl"></param>
113 /// <param name="rgb"></param>
114 public static void HSL2RGB(HSL hsl, RGB rgb)
115 {
116 if (hsl.Saturation == 0)
117 {
118 rgb.Red = rgb.Green = rgb.Blue = (byte) (hsl.Luminance * 255);
119 }
120 else
121 {
122 double v1, v2;
123 double hue = (double) hsl.Hue / 360;
124
125 v2 = (hsl.Luminance < 0.5) ? (hsl.Luminance * (1 + hsl.Saturation)) : ((hsl.Luminance + hsl.Saturation) - (hsl.Luminance * hsl.Saturation));
126 v1 = 2 * hsl.Luminance - v2;
127
128 rgb.Red = (byte)(255 * Hue_2_RGB(v1, v2, hue + (1.0 / 3)));
129 rgb.Green = (byte)(255 * Hue_2_RGB(v1, v2, hue));
130 rgb.Blue = (byte)(255 * Hue_2_RGB(v1, v2, hue - (1.0 / 3)));
131 }
132 }
133
134
135 Private members#region Private members
136 private static double Hue_2_RGB(double v1, double v2, double vH)
137 {
138 if (vH < 0)
139 vH += 1;
140 if (vH > 1)
141 vH -= 1;
142 if ((6 * vH) < 1)
143 return (v1 + (v2 - v1) * 6 * vH);
144 if ((2 * vH) < 1)
145 return v2;
146 if ((3 * vH) < 2)
147 return (v1 + (v2 - v1) * ((2.0 / 3) - vH) * 6);
148 return v1;
149 }
150 #endregion
151 }
152
153
154
155
156 public class HueModifier
157 {
158 private int hue = 0;
159
160 public int Hue
161 {
162 get { return hue; }
163 set { hue = System.Math.Max(0, System.Math.Min(359, value)); }
164 }
165
166 public HueModifier()
167 {
168 }
169
170 public HueModifier(int hue)
171 {
172 this.hue = hue;
173 }
174
175
176 public Bitmap Apply(Bitmap srcImg)
177 {
178 if (srcImg.PixelFormat != PixelFormat.Format24bppRgb)
179 throw new Exception("Sorry!");
180
181 int width = srcImg.Width;
182 int height = srcImg.Height;
183
184 BitmapData srcData = srcImg.LockBits(
185 new Rectangle(0, 0, width, height),
186 ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
187
188 Bitmap dstImg = new Bitmap(width, height, PixelFormat.Format24bppRgb);
189
190 BitmapData dstData = dstImg.LockBits(
191 new Rectangle(0, 0, width, height),
192 ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
193
194 RGB rgb = new RGB();
195 HSL hsl = new HSL();
196 int offset = srcData.Stride - width * 3;
197
198 unsafe
199 {
200 byte * src = (byte *) srcData.Scan0.ToPointer();
201 byte * dst = (byte *) dstData.Scan0.ToPointer();
202
203 for (int y = 0; y < height; y++) //循环,一个象素一个象素的转
204 {
205 for (int x = 0; x < width; x++, src += 3, dst += 3)
206 {
207 rgb.Red = src[RGB.R];
208 rgb.Green = src[RGB.G];
209 rgb.Blue = src[RGB.B];
210
211 ColorConverter.RGB2HSL(rgb, hsl);
212
213 hsl.Hue = hsl.Hue + hue;
214
215 ColorConverter.HSL2RGB(hsl, rgb);
216
217 dst[RGB.R] = rgb.Red;
218 dst[RGB.G] = rgb.Green;
219 dst[RGB.B] = rgb.Blue;
220 }
221 src += offset;
222 dst += offset;
223 }
224 }
225 dstImg.UnlockBits(dstData);
226 srcImg.UnlockBits(srcData);
227
228 return dstImg;
229 }
230 }
231}