zoukankan      html  css  js  c++  java
  • C#实现给图 片加透明度水印

    [转]C#实现给图 片加透明度水印

    首先我们强调一下opacity的概念,是“不透明度”,它表示的 是两个图层之间的关系,即该属性隶属于本图层,它表示与本图层下面图层之间的像素合成关系,当opacity=100%时,表示本图层完全不透明,因此下 面的像素完全被遮挡。当opacity=0时,表示本图层完全透明,即能看到下面的图层。这是photoshop中最基本的一个算法,表达如下:
            像素结果=底部图层*(1-opacity)+本图层*opacity;
            当有多个图层x0,x1,x2混合时,表达如下:(最底层的不透明度为1)
            x=((1-k1)x0+k1*x1)(1-k2)+k2*x2= (1-k1)(1-k2)x0 + k1(1-k2)x1 + k2*x2;

            那么绘制透明度水印的方法也就非常直观了,因为类库中的ImageAttributes属性里面并没有提供像素合成的绘制选项,因此我们自己实现上面的算 法。方法是:首先准备一个小的水印图片,我们先把原图在水印下面的部分绘制上去,然后在吧水印的文本或图片绘制上去,然后把原图和水印图片的内存数据锁定 (防止操作系统移动内存),然后直接用上面的算法改写原图的位图数据,解锁内存即得到最终加了水印的图片。

          代码如下:下面是绘制文本类型的水印,只需提供水印文本内容,绘制起始坐标即可。注意,为了简单直观起见,代码中都没有做参数验证,例如水印是否超出原图 范围,如果超出范围将引发对超过内存边界的访问限制(引发异常)。下面使用了unsafe代码,因此项目属性->Build中,应勾选允许不安全代 码,否则无法编译。 在下面代码中的定位方式是非常熟悉的。再次强调的是以下的概念:
          scan0:指针,内存数据的起始地址。(换句话说,就是指向第一个扫描行第一个像素的Blue)。
          bpp:bit per pixel。
          stride:扫描行宽度,=width*bpp/8 并在结尾补0~3个字节的0,以凑齐到4字节整数倍。
          之所以横坐标乘以3是因为我们锁定的方式是24bppRgb(最后一个参数指定了数据的bpp),这意味这每个像素占据了3个字节,因此i要乘以3来跳跃 到下一个像素。如果用32bppRGB锁定,则每个像素在内存占据4字节,相应的i应该乘以4。
    Code-文本水印
    /// <summary>
    /// 给一个位图绘制水印文字(没有验证水印是否超出图片边界!)
    /// </summary>
    /// <param name="text">水印文本</param>
    /// <param name="x">起始点</param>
    /// <param name="y">起始点</param>
    /// <param name="opacity">不透明度,0~1</param>
    private Bitmap DrawWatermark(Image image,string text, Font font,Brush brush,int x,int y,double opacity)
    {
        Bitmap bm1 = new Bitmap(image);
        Graphics g1=Graphics.FromImage(bm1);
        //测量水印文字的大 小,然后申请一个新的位图
        SizeF sizef=g1.MeasureString(text,font);
        Bitmap bm2=new Bitmap((int)sizef.Width,(int)sizef.Height);
        Graphics g2=Graphics.FromImage(bm2);
        g2.DrawImage(bm1, 0,0,new Rectangle(x, y, bm2.Width, bm2.Height),GraphicsUnit.Pixel);
        g2.DrawString(text,font,brush,0,0);
        BitmapData data1=bm1.LockBits(new Rectangle(0,0,bm1.Width,bm1.Height),ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);
        BitmapData data2=bm2.LockBits(new Rectangle(0,0,bm2.Width,bm2.Height),ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);
        unsafe
        {
            byte* p1=(byte*)(void*)data1.Scan0;
            byte* p2=(byte*)(void*)data2.Scan0;
            for(int j=0;j<bm2.Height;j++)
            {
                for(int i=0;i<bm2.Width*3;i++)
                {
                    p1[(y+j)*data1.Stride+i]=(byte)(p1[(y+j)*data1.Stride+i]*(1-opacity)+opacity*p2[j*data2.Stride+i]);
                }
            }
            bm1.UnlockBits(data1);
            bm2.UnlockBits(data2);
        }
        return bm1;
    }

    还有一种情况是,我们事先做作好水印,它是一个 图片,更多的人在photoshop中使用一个自己设计好的logo,保存为一个“画笔形状”,制作时只要选中此画笔一盖就好了。实际上这种类型的水印是 一个图片,为了加这种类型的水印,引入下面的overload方法:指定水印图片和透明色。
    Code-图片水印
    /// <param name="image">原 图</param>
    /// <param name="wmImg">水印图片</param>
    /// <param name="key">透明色</param>
    /// <param name="x">起始点</param>
    /// <param name="y"></param>
    /// <param name="opacity">不 透明度</param>
    /// <returns></returns>
    private Bitmap DrawWatermark(Image image, Bitmap wmImg, Color key,int x, int y, double opacity)
    {
        Bitmap bm1 = new Bitmap(image);    //克隆原图,它也是我们的返回值
        Bitmap bm2 = new Bitmap(wmImg.Width, wmImg.Height);    //准备的水印图片
        Graphics g2 = Graphics.FromImage(bm2);
        ImageAttributes att = new ImageAttributes();
        att.SetColorKey(key, key, ColorAdjustType.Bitmap);    //设定透明色
        g2.DrawImage(bm1, 0, 0, new Rectangle(x, y, bm2.Width, bm2.Height), GraphicsUnit.Pixel);
        g2.DrawImage(wmImg, new Rectangle(0,0,bm2.Width,bm2.Height),0, 0,bm2.Width,bm2.Height,GraphicsUnit.Pixel,att);
        g2.Dispose();
        。。。。这里的代码和上面的方法代码相同,因此省略
        return bm1;
    }

    以 上两种效果的截图:
    (1)    (2)
             
            源代码的下载链接:(该项目里还包括我对Photoshop中置换滤镜的模拟代码,以及在水波特效控件原理解释那篇文中的水波置换图生成器。)
    源代码下载
    原 文地址:http://www.netcsharp.cn/showtopic-1174.aspx


       本人博客的文章大部分来自网络转载,因为时间的关系,没有写明转载出处和作者。所以在些郑重的说明:文章只限交流,版权归作者。谢谢

  • 相关阅读:
    【Leetcode】【Easy】Remove Duplicates from Sorted List
    【Leetcode】【Easy】Pascal's Triangle II
    【Leetcode】【Easy】Pascal's Triangle
    【Leetcode】【Easy】Binary Tree Level Order Traversal II
    【Leetcode】【Easy】Binary Tree Level Order Traversal
    【Leetcode】【Easy】Maximum Depth of Binary Tree
    【Leetcode】【Easy】Minimum Depth of Binary Tree
    【Leetcode】【Easy】Balanced Binary Tree
    【Leetcode】【Easy】Symmetric Tree
    如何使用Action.Invoke()触发一个Storyboard
  • 原文地址:https://www.cnblogs.com/wzg0319/p/1705528.html
Copyright © 2011-2022 走看看