水印的生成,起初我只是把它看成一个图片合成的简单过程,充其量也就是再加个追加文字。一开始,我写了一个demo,其实功能而言基本和后面重写的这个没有太大差异,但是因为目的性极强,完全忽略了代码上的可靠性,当时甚至更多的只是关注如何实现透明度的控制。
不过当功能的实现这个槛过了以后,我就反过来关注起给图片加水印的另外一些细节,比如水印的组成,位置等与水印本身同样重要的部分。
从选择水印图片、文字以及位置到最终生成,这个操作过程其实不过就是几次点击。而在生成水印图片之前,操作的部分没有严格的顺序,既不存在先选择位置,还是先选择水印内容的前后之分,代码的执行都在点击了“保存”以后才真正开始,因此,为了将水印相关的各个部分的耦合性降到最低,我作了以下考虑。
由于水印的部分可以是文字,可以是图片,不管怎么样,我们把它看做水印的CONTENT;另外,不管是什么类型的CONTENT,都有一个具体的位置信息。而我们在操作水印生成的过程的时候,选择位置和水印图片或文字的时候都是独立不相约束的,所以就有了最初的两个独立的对象:
·位置对象
·水印对象
1.位置
原来我考虑的水印位置只有根据ContentAlignment定位以及平铺的形式,但是后来同事提了个建议说是否可以自己定义水印出现的位置,所以就有了AlignedPosition与CustomedPosition的两个WatermarkPosition的子类。
从上面的图可以看出,AlignedPosition有两个子类:SingleAlignedPosition、RepeatAlignedPosition。这两个类就是之前我提到的以ContentAlignment定位的和以平铺形式分布的水印位置对象。
另外,由于平铺的水印的位置不止一个,所以这里引入了一个IMultiPosition。
代码:
WatermarkPosition.cs
1public abstract class WatermarkPosition
2{
3 protected float _x;
4 protected float _y;
5
6 public abstract RectangleF GetPosition(Size imageSize, Size watermarkSize);
7}
AlignedPosition.cs
1public abstract class AlignedPosition : WatermarkPosition
2{
3 protected float _offsetX;
4 protected float _offsetY;
5
6 Cstr#region Cstr
7 public AlignedPosition()
8 {
9 this._offsetX = -1.0f;
10 this._offsetY = -1.0f;
11 }
12 #endregion
13
14 public override RectangleF GetPosition(Size imageSize, Size watermarkSize)
15 {
16 if (this._offsetX == -1.0f && this._offsetY == -1.0f)
17 {
18 float tWidht = (float)imageSize.Width;
19 float tHeight = (float)imageSize.Height;
20
21 this._offsetX = tWidht * 0.05f;
22 this._offsetY = tHeight * 0.05f;
23 }
24
25 return this.GetRectangle(imageSize, watermarkSize);
26 }
27
28 protected abstract RectangleF GetRectangle(Size imageSize, Size watermarkSize);
29}
SingleAlignedPosition.cs
1public class SingleAlignedPosition : AlignedPosition
2{
3 private ContentAlignment _alignment;
4
5 Cstr#region Cstr
6 public SingleAlignedPosition() : base()
7 {
8 this._alignment = ContentAlignment.TopLeft;
9 }
10
11 public SingleAlignedPosition(ContentAlignment alignment) : base()
12 {
13 this._alignment = alignment;
14 }
15
16 public SingleAlignedPosition(ContentAlignment alignment, int offset)
17 {
18 this._alignment = alignment;
19
20 this._offsetX = (float)offset;
21 this._offsetY = (float)offset;
22 }
23 #endregion
24
25 protected override RectangleF GetRectangle(Size imageSize, Size watermarkSize)
26 {
27 float x, y;
28
29 float tWidth = (float)imageSize.Width;
30 float tHeight = (float)imageSize.Height;
31 float width = (float)watermarkSize.Width;
32 float height = (float)watermarkSize.Height;
33
34 switch(this._alignment)
35 {
36 case ContentAlignment.TopLeft: // 左上
37 x = this._offsetX;
38 y = this._offsetY;
39 break;
40 case ContentAlignment.TopCenter: // 上中
41 x = (tWidth - width)/2;
42 y = this._offsetY;
43 break;
44 case ContentAlignment.TopRight: // 右上
45 x = tWidth - width - this._offsetX;
46 y = this._offsetY;
47 break;
48 case ContentAlignment.MiddleLeft: // 左中:
49 x = this._offsetX;
50 y = (tHeight - tHeight)/2;
51 break;
52 case ContentAlignment.MiddleCenter: // 正中央
53 x = (tWidth - width)/2;
54 y = (tHeight - height)/2;
55 break;
56 case ContentAlignment.MiddleRight: // 右中
57 x = tWidth - width - this._offsetX;
58 y = (tHeight - height)/2;
59 break;
60 case ContentAlignment.BottomLeft: // 左下
61 x = this._offsetX;
62 y = tHeight - height - this._offsetY;
63 break;
64 case ContentAlignment.BottomCenter: // 左中
65 x = (imageSize.Width - width)/2;
66 y = tHeight - height - this._offsetY;
67 break;
68 default: // 默认右下
69 x = tWidth - width - this._offsetX;
70 y = tHeight - height - this._offsetY;
71 break;
72 }
73
74 return new RectangleF(x, y, width, height);
75 }
76}
RepeatAlignedPosition.cs
1public class RepeatAlignedPosition : AlignedPosition, IMultiPosition
2{
3 private float _currentX = 0.0f;
4 private float _currentY = 0.0f;
5 private bool _moveFlag = true;
6
7 Cstr#region Cstr
8 public RepeatAlignedPosition() : base()
9 {
10 }
11
12 public RepeatAlignedPosition(int offset)
13 {
14 this._offsetX = offset;
15 this._offsetY = offset;
16 }
17 #endregion
18
19 protected override RectangleF GetRectangle(Size imageSize, Size watermarkSize)
20 {
21 if (!this._moveFlag)
22 {
23 throw new InvalidOperationException("请确认所有位置是否都已输出,否则必须调用MoveNext才能取得位置对象");
24 }
25 else
26 {
27 this._currentX += this._offsetX;
28 this._currentY += this._offsetY;
29
30 this._moveFlag = false;
31
32 return new RectangleF(this._currentX, this._currentY, (float)watermarkSize.Width, (float)watermarkSize.Height);
33
34 //this.MoveNext(imageSize, watermarkSize);
35 }
36 }
37
38 IMultiPosition 成员#region IMultiPosition 成员
39 RectangleF[] Jingstudio.WatermarkMaker.Watermark.IMultiPosition.GetPositions(Size imageSize, Size watermarkSize)
40 {
41 float tWidth = (float)imageSize.Width;
42 float tHeight = (float)imageSize.Height;
43 float width = (float)watermarkSize.Width;
44 float height = (float)watermarkSize.Height;
45
46 int x = (int)Math.Ceiling(tWidth / (width + this._offsetX));
47 int y = (int)Math.Ceiling(tHeight / (height + this._offsetY));
48
49 RectangleF[] positions = new RectangleF[x*y];
50
51 int index = 0;
52 for (int i=0; i<x; i++)
53 {
54 for (int j=0; j<y; j++)
55 {
56 PointF p = new PointF( (float)i * (width + this._offsetX), (float)j * (height + this._offsetY) );
57 positions[index] = new RectangleF(p, watermarkSize);
58 index ++;
59 }
60 }
61
62 return positions;
63 }
64
65 public bool MoveNext(Size imageSize, Size watermarkSize)
66 {
67 float tWidth = (float)imageSize.Width;
68 float tHeight = (float)imageSize.Height;
69 float width = (float)watermarkSize.Width;
70 float height = (float)watermarkSize.Height;
71 float x, y;
72 x = 0; y = 0;
73
74 x = this._currentX + this._offsetX + width;
75
76 if (x >= tWidth)
77 {
78 x = this._offsetX;
79 y = this._currentY + this._offsetY + height;
80 }
81
82 if (y >= tHeight)
83 {
84 return false;
85 }
86 else
87 {
88 this._currentX = x;
89 this._currentY = y;
90 this._moveFlag = true;
91 return true;
92 }
93 }
94 #endregion
95}
CustomedPosition.cs
1public class CustomedPosition : WatermarkPosition
2{
3 Cstr#region Cstr
4 public CustomedPosition()
5 {
6 this._x = -1;
7 this._y = -1;
8 }
9
10 public CustomedPosition(PointF point)
11 {
12 this._x = point.X;
13 this._y = point.Y;
14 }
15 #endregion
16
17 public override RectangleF GetPosition(Size imageSize, Size watermarkSize)
18 {
19 float width = (float)watermarkSize.Width;
20 float height = (float)watermarkSize.Height;
21
22 if (this._x == -1 && this._y == -1)
23 {
24 float tWidth = (float)imageSize.Width;
25 float tHeight = (float)imageSize.Height;
26
27 this._x = (tWidth - width)/2;
28 this._y = (tHeight - height)/2;
29 }
30
31 return new RectangleF (this._x, this._y, width, height);
32 }
33}