水印的生成,起初我只是把它看成一个图片合成的简单过程,充其量也就是再加个追加文字。一开始,我写了一个demo,其实功能而言基本和后面重写的这个没有太大差异,但是因为目的性极强,完全忽略了代码上的可靠性,当时甚至更多的只是关注如何实现透明度的控制。 不过当功能的实现这个槛过了以后,我就反过来关注起给图片加水印的另外一些细节,比如水印的组成,位置等与水印本身同样重要的部分。 从选择水印图片、文字以及位置到最终生成,这个操作过程其实不过就是几次点击。而在生成水印图片之前,操作的部分没有严格的顺序,既不存在先选择位置,还是先选择水印内容的前后之分,代码的执行都在点击了“保存”以后才真正开始,因此,为了将水印相关的各个部分的耦合性降到最低,我作了以下考虑。 由于水印的部分可以是文字,可以是图片,不管怎么样,我们把它看做水印的CONTENT;另外,不管是什么类型的CONTENT,都有一个具体的位置信息。而我们在操作水印生成的过程的时候,选择位置和水印图片或文字的时候都是独立不相约束的,所以就有了最初的两个独立的对象:
·位置对象 ·水印对象
1.位置 原来我考虑的水印位置只有根据ContentAlignment定位以及平铺的形式,但是后来同事提了个建议说是否可以自己定义水印出现的位置,所以就有了AlignedPosition与CustomedPosition的两个WatermarkPosition的子类。
从上面的图可以看出,AlignedPosition有两个子类:SingleAlignedPosition、RepeatAlignedPosition。这两个类就是之前我提到的以ContentAlignment定位的和以平铺形式分布的水印位置对象。 另外,由于平铺的水印的位置不止一个,所以这里引入了一个IMultiPosition。 代码: WatermarkPosition.cs
AlignedPosition.cs
SingleAlignedPosition.cs
RepeatAlignedPosition.cs
CustomedPosition.cs
2.水印 前文讲了水印位置的基本思路以及代码,接下来就是水印的实体类了。前面讲了我把水印的组成分为水印位置与水印内容两个对象,但是,实现的时候,界限就不是那么明显。 我提供了一个Watermark的实体类,而它只是作为一个抽象的实体,由于水印包括了图片与文字两种形式,所以由此类衍生出ImageWatermark与TextWatermark两个子类。由这两个类来实现具体的水印内容的区分。
ImageWatermark拥有一个内嵌类(Nested Class)ImageProperties,表示水印的图片信息。 TextWatermark拥有一个内嵌类TextProperties,表示水印文字的属性。 Watermark对象提供了对这两个具体的水印内容的聚合。 这里有个问题,是把水印内容建一个抽象类,然后衍生不同的水印内容(图片、文字)好呢,还是以我这样的设计好。我个人觉得,我这么做是为了从水印(Watermark)的角度去区别对象——即可以直接从ImageWatermark和TextWatermark来区分是何种水印方式,而不是再从其内容来区分。这个对后面的水印生成类的定义应该是有帮助的。 另外,Watermark也提供了对之前的Position的聚合
复制C#代码保存代码
public abstract class WatermarkPosition
{
protected float _x;
protected float _y;
public abstract RectangleF GetPosition(Size imageSize, Size watermarkSize);
}
复制C#代码保存代码
public abstract class AlignedPosition : WatermarkPosition
{
protected float _offsetX;
protected float _offsetY;
#region Cstr
public AlignedPosition()
{
this._offsetX = -1.0f;
this._offsetY = -1.0f;
}
#endregion
public override RectangleF GetPosition(Size imageSize, Size watermarkSize)
{
if (this._offsetX == -1.0f && this._offsetY == -1.0f)
{
float tWidht = (float)imageSize.Width;
float tHeight = (float)imageSize.Height;
this._offsetX = tWidht * 0.05f;
this._offsetY = tHeight * 0.05f;
}
return this.GetRectangle(imageSize, watermarkSize);
}
protected abstract RectangleF GetRectangle(Size imageSize, Size watermarkSize);
}
复制C#代码保存代码
public class SingleAlignedPosition : AlignedPosition
{
private ContentAlignment _alignment;
#region Cstr
public SingleAlignedPosition()
: base()
{
this._alignment = ContentAlignment.TopLeft;
}
public SingleAlignedPosition(ContentAlignment alignment)
: base()
{
this._alignment = alignment;
}
public SingleAlignedPosition(ContentAlignment alignment, int offset)
{
this._alignment = alignment;
this._offsetX = (float) offset;
this._offsetY = (float) offset;
}
#endregion
protected override RectangleF GetRectangle(Size imageSize, Size watermarkSize)
{
float x, y;
float tWidth = (float) imageSize.Width;
float tHeight = (float) imageSize.Height;
float width = (float) watermarkSize.Width;
float height = (float) watermarkSize.Height;
switch (this._alignment)
{
case ContentAlignment.TopLeft: // 左上
x = this._offsetX;
y = this._offsetY;
break;
case ContentAlignment.TopCenter: // 上中
x = (tWidth - width) / 2;
y = this._offsetY;
break;
case ContentAlignment.TopRight: // 右上
x = tWidth - width - this._offsetX;
y = this._offsetY;
break;
case ContentAlignment.MiddleLeft: // 左中:
x = this._offsetX;
y = (tHeight - tHeight) / 2;
break;
case ContentAlignment.MiddleCenter: // 正中央
x = (tWidth - width) / 2;
y = (tHeight - height) / 2;
break;
case ContentAlignment.MiddleRight: // 右中
x = tWidth - width - this._offsetX;
y = (tHeight - height) / 2;
break;
case ContentAlignment.BottomLeft: // 左下
x = this._offsetX;
y = tHeight - height - this._offsetY;
break;
case ContentAlignment.BottomCenter: // 左中
x = (imageSize.Width - width) / 2;
y = tHeight - height - this._offsetY;
break;
default: // 默认右下
x = tWidth - width - this._offsetX;
y = tHeight - height - this._offsetY;
break;
}
return new RectangleF(x, y, width, height);
}
}
复制C#代码保存代码
public class RepeatAlignedPosition : AlignedPosition, IMultiPosition
{
private float _currentX = 0.0f;
private float _currentY = 0.0f;
private bool _moveFlag = true;
#region Cstr
public RepeatAlignedPosition()
: base()
{
}
public RepeatAlignedPosition(int offset)
{
this._offsetX = offset;
this._offsetY = offset;
}
#endregion
protected override RectangleF GetRectangle(Size imageSize, Size watermarkSize)
{
if (!this._moveFlag)
{
throw new InvalidOperationException("请确认所有位置是否都已输出,否则必须调用MoveNext才能取得位置对象");
}
else
{
this._currentX += this._offsetX;
this._currentY += this._offsetY;
this._moveFlag = false;
return new RectangleF(this._currentX, this._currentY, (float) watermarkSize.Width, (float) watermarkSize.Height);
//this.MoveNext(imageSize, watermarkSize);
}
}
#region IMultiPosition 成员
RectangleF[] Jingstudio.WatermarkMaker.Watermark.IMultiPosition.GetPositions(Size imageSize, Size watermarkSize)
{
float tWidth = (float) imageSize.Width;
float tHeight = (float) imageSize.Height;
float width = (float) watermarkSize.Width;
float height = (float) watermarkSize.Height;
int x = (int) Math.Ceiling(tWidth / (width + this._offsetX));
int y = (int) Math.Ceiling(tHeight / (height + this._offsetY));
RectangleF[] positions = new RectangleF[x * y];
int index = 0;
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
PointF p = new PointF((float) i * (width + this._offsetX), (float) j * (height + this._offsetY));
positions[index] = new RectangleF(p, watermarkSize);
index++;
}
}
return positions;
}
public bool MoveNext(Size imageSize, Size watermarkSize)
{
float tWidth = (float) imageSize.Width;
float tHeight = (float) imageSize.Height;
float width = (float) watermarkSize.Width;
float height = (float) watermarkSize.Height;
float x, y;
x = 0;
y = 0;
x = this._currentX + this._offsetX + width;
if (x >= tWidth)
{
x = this._offsetX;
y = this._currentY + this._offsetY + height;
}
if (y >= tHeight)
{
return false;
}
else
{
this._currentX = x;
this._currentY = y;
this._moveFlag = true;
return true;
}
}
#endregion
}
复制C#代码保存代码
public class CustomedPosition : WatermarkPosition
{
#region Cstr
public CustomedPosition()
{
this._x = -1;
this._y = -1;
}
public CustomedPosition(PointF point)
{
this._x = point.X;
this._y = point.Y;
}
#endregion
public override RectangleF GetPosition(Size imageSize, Size watermarkSize)
{
float width = (float)watermarkSize.Width;
float height = (float)watermarkSize.Height;
if (this._x == -1 && this._y == -1)
{
float tWidth = (float)imageSize.Width;
float tHeight = (float)imageSize.Height;
this._x = (tWidth - width)/2;
this._y = (tHeight - height)/2;
}
return new RectangleF (this._x, this._y, width, height);
}
}