您的位置:首页 > 编程语言 > C#

C#水印图片的生成 - 教你如何用C#程序生成水印图片

2009-12-01 00:09 471 查看
Watermark Maker Classes I: Brief Intro, WatermarkP

水印的生成,起初我只是把它看成一个图片合成的简单过程,充其量也就是再加个追加文字。一开始,我写了一个demo,其实功能而言基本和后面重写的这个没有太大差异,但是因为目的性极强,完全忽略了代码上的可靠性,当时甚至更多的只是关注如何实现透明度的控制。

不过当功能的实现这个槛过了以后,我就反过来关注起给图片加水印的另外一些细节,比如水印的组成,位置等与水印本身同样重要的部分。

从选择水印图片、文字以及位置到最终生成,这个操作过程其实不过就是几次点击。而在生成水印图片之前,操作的部分没有严格的顺序,既不存在先选择位置,还是先选择水印内容的前后之分,代码的执行都在点击了“保存”以后才真正开始,因此,为了将水印相关的各个部分的耦合性降到最低,我作了以下考虑。

由于水印的部分可以是文字,可以是图片,不管怎么样,我们把它看做水印的CONTENT;另外,不管是什么类型的CONTENT,都有一个具体的位置信息。而我们在操作水印生成的过程的时候,选择位置和水印图片或文字的时候都是独立不相约束的,所以就有了最初的两个独立的对象:

·位置对象
·水印对象


1.位置

原来我考虑的水印位置只有根据ContentAlignment定位以及平铺的形式,但是后来同事提了个建议说是否可以自己定义水印出现的位置,所以就有了AlignedPosition与CustomedPosition的两个WatermarkPosition的子类。




从上面的图可以看出,AlignedPosition有两个子类:SingleAlignedPosition、RepeatAlignedPosition。这两个类就是之前我提到的以ContentAlignment定位的和以平铺形式分布的水印位置对象。

另外,由于平铺的水印的位置不止一个,所以这里引入了一个IMultiPosition。

代码:

WatermarkPosition.cs

复制C#代码保存代码

public abstract class WatermarkPosition
{
    protected float _x;
    protected float _y;

    public abstract RectangleF GetPosition(Size imageSize, Size watermarkSize);
}


AlignedPosition.cs

复制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); 
}


SingleAlignedPosition.cs

复制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);
     }
}


RepeatAlignedPosition.cs

复制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
}


CustomedPosition.cs

复制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); 
     } 
}


2.水印
前文讲了水印位置的基本思路以及代码,接下来就是水印的实体类了。前面讲了我把水印的组成分为水印位置与水印内容两个对象,但是,实现的时候,界限就不是那么明显。

我提供了一个Watermark的实体类,而它只是作为一个抽象的实体,由于水印包括了图片与文字两种形式,所以由此类衍生出ImageWatermark与TextWatermark两个子类。由这两个类来实现具体的水印内容的区分。





ImageWatermark拥有一个内嵌类(Nested Class)ImageProperties,表示水印的图片信息。
TextWatermark拥有一个内嵌类TextProperties,表示水印文字的属性。

Watermark对象提供了对这两个具体的水印内容的聚合。

这里有个问题,是把水印内容建一个抽象类,然后衍生不同的水印内容(图片、文字)好呢,还是以我这样的设计好。我个人觉得,我这么做是为了从水印(Watermark)的角度去区别对象——即可以直接从ImageWatermark和TextWatermark来区分是何种水印方式,而不是再从其内容来区分。这个对后面的水印生成类的定义应该是有帮助的。

另外,Watermark也提供了对之前的Position的聚合



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: