//添加自定义控件 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