C# Winform 水波纹效果

C# Winform 水波纹效果

//添加自定义控件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;

namespace My.UControl
{ 
    public partial class WaveControl : Panel// Control
    {
        public WaveControl()
        {
            InitializeComponent();
            this.Effect.Tick += new EventHandler(Effect_Tick);
            this.Paint += new PaintEventHandler(WavesControl_Paint);
            this.MouseMove += new MouseEventHandler(WavesControl_MouseMove);

            this.Effect.Enabled = true;
            this.Effect.Interval = 50;
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
            this.BackColor = Color.White;
        }
        public WaveControl(Bitmap bmp)
            : this()
        {
            this.bmpImage = bmp;
        }
        #region Fields or Properties
        private int scale = 1;
        /// <summary>
        /// The scale of the wave matrix compared to the size of the image.
        /// Use it for large images to reduce processor load.
        /// 
        /// 0 : wave resolution is the same than image resolution
        /// 1 : wave resolution is half the image resolution
        /// ...and so on
        /// </summary>
        public int Scale
        {
            get { return scale; }
            set { scale = value; }
        }
        private Bitmap bmpImage;
        /// <summary>
        /// Background image
        /// </summary>
        public Bitmap BmpImage
        {
            get { return bmpImage; }
            set
            {
                if (value == null)
                    return;
                bmpImage = value;
                bmpHeight = bmpImage.Height;
                bmpWidth = bmpImage.Width;
                waveHeight = bmpHeight >> scale;
                waveWidth = bmpWidth >> scale;

                waves = new short[waveWidth, waveHeight, 2];

                bmpBytes = new byte[bmpWidth * bmpHeight * 4];

                bmpBitmapData = bmpImage.LockBits(new Rectangle(0, 0, bmpWidth, bmpHeight),
                    System.Drawing.Imaging.ImageLockMode.ReadWrite,
                    System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                Marshal.Copy(bmpBitmapData.Scan0, bmpBytes, 0, bmpWidth * bmpHeight * 4);
            }
        }

        private int bmpHeight { get; set; }
        private int bmpWidth { get; set; }

        private int waveWidth { get; set; }
        private int waveHeight { get; set; }

        private short[, ,] waves { get; set; }
        private byte[] bmpBytes { get; set; }

        private System.Drawing.Imaging.BitmapData bmpBitmapData { get; set; }
        private bool IsWaves = false;

        private int activedBuf = 0;
        #endregion

        void WavesControl_MouseMove(object sender, MouseEventArgs e)
        {
            int realX = (int)((e.X / (double)this.ClientRectangle.Width) * waveWidth);
            int realY = (int)((e.Y / (double)this.ClientRectangle.Height) * waveHeight);
            this.PutDrop(realX, realY, 200);
        }
        /// <summary>
        /// This function is used to start a wave by simulating a round drop
        /// </summary>
        /// <param name="realX">x position of the drop</param>
        /// <param name="realY">y position of the drop</param>
        /// <param name="height">Height position of the drop</param>
        private void PutDrop(int realX, int realY, int height)
        {
            this.IsWaves = true;
            int radius =20;
            double dist;
            for (int i = -radius; i <= radius; i++)
            {
                for (int j = -radius; j <= radius; j++)
                {
                    if (((realX + i >= 0) && (realX + i < waveWidth - 1)) && ((realY + j >= 0) && (realY + j < waveHeight - 1)))
                    {
                        dist = Math.Sqrt(i * i + j * j);
                        if (dist < radius)
                            waves[realX + i, realY + j, activedBuf] = (short)(Math.Cos(dist * Math.PI / radius) * height);
                    }
                }
            }
        }
        /// <summary>
        /// Paint handler
        /// Calculates the final effect-image out of
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void WavesControl_Paint(object sender, PaintEventArgs e)
        {
            if (bmpImage == null) return;
            using (Bitmap tmpBmp = bmpImage.Clone() as Bitmap)
            {

                int xOffset, yOffset;
                byte alpha;
                if (IsWaves)
                {
                    BitmapData tmpBitmapData = tmpBmp.LockBits(new Rectangle(0, 0, bmpWidth, bmpHeight),
                        ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                    byte[] tmpBytes = new byte[bmpWidth * bmpHeight * 4];
                    Marshal.Copy(tmpBitmapData.Scan0, tmpBytes, 0, bmpWidth * bmpHeight * 4);
                    for (int x = 1; x < bmpWidth - 1; x++)
                    {
                        for (int y = 1; y < bmpHeight - 1; y++)
                        {
                            int waveX = (int)x >> scale;
                            int waveY = (int)y >> scale;
                            ///check bounds
                            waveX = waveX <= 0 ? 1 : waveX;
                            waveY = waveY <= 0 ? 1 : waveY;
                            waveX = waveX >= waveWidth - 1 ? waveWidth - 2 : waveX;
                            waveY = waveY >= waveHeight - 1 ? waveHeight - 2 : waveY;
                            ///this gives us the effect of water breaking the light
                            xOffset = (waves[waveX - 1, waveY, activedBuf] - waves[waveX + 1, waveY, activedBuf]) >> 3;
                            yOffset = (waves[waveX, waveY - 1, activedBuf] - waves[waveX, waveY + 1, activedBuf]) >> 3;

                            if ((xOffset != 0) || (yOffset != 0))
                            {
                                ///check bounds
                                if (x + xOffset >= bmpWidth - 1)
                                    xOffset = bmpWidth - x - 1;
                                if (y + yOffset >= bmpHeight - 1)
                                    yOffset = bmpHeight - y - 1;
                                if (x + xOffset < 0) xOffset = -x;
                                if (y + yOffset < 0) yOffset = -y;
                                ///generate alpha
                                alpha = (byte)(200 - xOffset);
                                if (alpha < 0) alpha = 0;
                                if (alpha > 255) alpha = 254;
                                ///set colors
                                tmpBytes[4 * (x + y * bmpWidth)] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth)];
                                tmpBytes[4 * (x + y * bmpWidth) + 1] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth) + 1];
                                tmpBytes[4 * (x + y * bmpWidth) + 2] = bmpBytes[4 * (x + xOffset + (y + yOffset) * bmpWidth) + 2];
                                tmpBytes[4 * (x + y * bmpWidth) + 3] = alpha;
                            }
                        }
                    }
                    ///copy data back
                    Marshal.Copy(tmpBytes, 0, tmpBitmapData.Scan0, bmpWidth * bmpHeight * 4);
                    tmpBmp.UnlockBits(tmpBitmapData);
                }
                e.Graphics.DrawImage(tmpBmp, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
            }
        }

        /// <summary>
        /// This is the method that actually does move the waves around and simulates the
        /// behaviour of water.
        /// </summary>
        private void Waves()
        {
            int newBuf = this.activedBuf == 0 ? 1 : 0;
            bool wavesFound = false;
            for (int x = 1; x < waveWidth - 1; x++)
            {
                for (int y = 1; y < waveHeight - 1; y++)
                {
                    waves[x, y, newBuf] = (short)(((
                        waves[x - 1, y - 1, activedBuf] +
                        waves[x - 1, y, activedBuf] +
                        waves[x - 1, y + 1, activedBuf] +
                        waves[x, y - 1, activedBuf] +
                        waves[x, y + 1, activedBuf] +
                        waves[x + 1, y - 1, activedBuf] +
                        waves[x + 1, y, activedBuf] +
                        waves[x + 1, y + 1, activedBuf]) >> 2) - waves[x, y, newBuf]);
                    ///damping
                    if (waves[x, y, newBuf] != 0)
                    {
                        waves[x, y, newBuf] -= (short)(waves[x, y, newBuf] >> 4);
                        wavesFound = true;
                    }
                }
            }
            IsWaves = wavesFound;
            activedBuf = newBuf;
        }

        void Effect_Tick(object sender, EventArgs e)
        {
            if (IsWaves)
            {
                Invalidate();
                Waves();
            }
        } 
        //protected override void OnPaint(PaintEventArgs pe)
        //{
        //    base.OnPaint(pe);
        //}
    }
}

 

参考资料[含程序,源码,算法]

http://pan.baidu.com/s/1dD3s2xN

http://www.cnblogs.com/worldreason/archive/2008/05/09/1189648.html

http://www.codeproject.com/Articles/1073/Interactive-water-effect

C# Winform 水波纹效果

上一篇:C#几种截取字符串的方法小结


下一篇:C#获取本周、上周、本月、上月、本季度、上季度、本年、上一年起始时间和结束时间