C#版水波效果源程序

一个很不错的c#实现的图片特效,相应源程序如下:

FASTBitmap.cs

using System;

using System.Collections.Generic;

using System.Text;

using System.Drawing;

using System.Drawing.Imaging;

namespace WaveEffects

{

    public unsafe class FastBitmap {

        public struct PixelData {

            public byte blue;

            public byte green;

            public byte red;

            public byte alpha;

        }

        Bitmap Subject;

        int SubjectWidth;

        BitmapData bitmapData = null;

        Byte* pBase = null;

        bool isLocked = false;

        int _bits = 0;

        public FastBitmap(Bitmap SubjectBitmap, int bits) {

            this.Subject = SubjectBitmap;

            _bits = bits;

            try {

                //LockBits();

            } catch (Exception ex) {

                throw ex;

            }            

        }

        public void Release() {

            try {

                UnlockBits();

            } catch (Exception ex) {

                throw ex;

            }

        }

        public Bitmap Bitmap {

            get {

                return Subject;

            }

        }

        public void SetPixel(int X, int Y, Color Colour) {

            try {

                PixelData* p = PixelAt(X, Y);

                p->red = Colour.R;

                p->green = Colour.G;

                p->blue = Colour.B;

            } catch (AccessViolationException ave) {

                throw (ave);

            } catch (Exception ex) {

                throw ex;

            }

        }

        public Color GetPixel(int X, int Y) {

            try {

                PixelData* p = PixelAt(X, Y);

                return Color.FromArgb((int)p->red, (int)p->green, (int)p->blue);

            } catch (AccessViolationException ave) {

                throw (ave);

            } catch (Exception ex) {

                throw ex;

            }            

        }

        public int Width() { return Subject.Width; }

        public int Height() { return Subject.Height; }

        public bool  IsLocked() { return isLocked; }

        public BitmapData Data() { return bitmapData; }

        public void LockBits() {

            if (isLocked) return;

            try

            {                

                GraphicsUnit unit = GraphicsUnit.Pixel;

                RectangleF boundsF = Subject.GetBounds(ref unit);

                Rectangle bounds = new Rectangle((int)boundsF.X,

                    (int)boundsF.Y,

                    (int)boundsF.Width,

                    (int)boundsF.Height);

                SubjectWidth = (int)boundsF.Width * sizeof(PixelData);

                if (SubjectWidth % _bits != 0)

                {

                    SubjectWidth = _bits * (SubjectWidth / _bits + 1);

                }

                if (_bits == 3)

                    bitmapData = Subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                else 

                    bitmapData = Subject.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);

                pBase = (Byte*)bitmapData.Scan0.ToPointer();

            }

            finally

            {

                isLocked = true ;

            }

        }

        private PixelData* PixelAt(int x, int y) {

            return (PixelData*)(pBase + y * SubjectWidth + x * sizeof(PixelData));            

        }

        private void UnlockBits() {

            if (bitmapData == null) return;

            Subject.UnlockBits(bitmapData);

            bitmapData = null;

            pBase = null;

            isLocked = false;

        }

    }

}

main.cs

//

//  Wave motion simulation. C# version                                                                          //

//  Written by Angel Rapallo 2007                                                                               //

//  I should have called this RAIN simulation, it makes more sense.                                             //

//    'Download by http://www.codefans.net                                                                                                          //

// ORIGINAL:                                                                                                    //

//  Original Algorithm by Don Pattee at http://www.x-caiver.com/Software and Christian Tratz.                   //

//  This version though still maintains at the core the original mathematical algorithm has                     //

//  been changed quite a bit. For one thing it out performs the previous versions and uses                      //

//  a class for FAST bitmap manipulation which uses UNSAFE C# and can be found at                               //

//  http://www.mdibb.net/net/faster_pixel_manipulation_with_getpixel_and_setpixel_in_net/                       //

//                                                                                                              //

// THIS:                                                                                                        //

//  Besides improving the original algorithm I also wrote it using different ARRAY structures                   //

//  JAGGED, RECTANGULAR,LINEAR etc... to see if there was anything to the Myth of Arrays, I found               //

//  there is really some difference but not what I had seen before and not as far as C# 2.0 is concerned.       //

//  I also wrote the algorithm using two techniques BUFFERED and UNBUFFERED rendering.                          //

//                                                                                                              //

//  Though I am sure this code will yield different results in different machines this is how I see it in my    //

//  personal Laptop Dell Inspiron 8500 running a Pentium 2.0 with 1 GYG of RAM.                                 //

//      #1 JAGGED      ARRAY USING BUGGER RENDERING                                                             //

//      #2 RECTANGULAR ARRAY USING BUGGER RENDERING                                                             //

//      #1 LINEAR      ARRAY USING BUGGER RENDERING                                                             //

//  Non buffered version ranked last. Thouggh I still think the FASTBitmap is a pretty good shot.               //

//                                                                                                              //

// MATH:                                                                                                        //

//  About the math, this algorithm is one among many, it is not the best out there but it is not the            //

//  worst. I have found other simpler algorithms written in java which perform better but the effects           //

//  are not that good. There are some websites out there which explain the math part i cant recall              //

//  the ones i read at the moment (i should have), but the idea is quite simple, a drop is placed               //

//  on the view by calculating some height based on the distance of the PIXEL from the center                   //

//  of the drop, then the motion algorithm kicks in, it calculates the height of the pixel                      //

//  using the height of the surrounding pixels and dampering the waves as it moves forward                      //

//  i have commented out places were you can play with different values, and the original versions              //

//  also have plenty of explanation. I think if you really want to learn water motion simulation                //

//  you should get a good book. This code uses COS() but it could as well use SIN() since one is the            //

//  same as the other shifted by 90 degrees. The original version also used COS().                              //

//                                                                                                              //

// CONDITIONS:                                                                                                  //

//  This code is posted with the same permission as the previous versions were, that is I maintain the          //

//  sharing conditions the previous developers who wrote the previous versions gave.                            //

//                                                                                                              //

// FEEDBACK:                                                                                                    //

//  I would like anyone who uses this code to notify me of anything that might proove interesting               //

//  and please dont forget the original contributors too, I ma sure they want to hear from you,                 //

//  though I could get an email for any of them at their websites.                                              //

//

//

//  Play with these to see what you get in your machine.

//

#define _BUFFERED_RENDERING

#define _JAGGED_ARRAYS

//#define _RECTANGULAR_ARRAYS

//#define _LINEAR_ARRAYS

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Drawing.Imaging;

using System.Runtime.InteropServices;

using System.Text;

using System.Windows.Forms;

using System.Reflection.Emit;

using System.Reflection;

namespace WaveEffects

{

    public partial class Main : Form

    {

        private struct DropData

        {

            public int x;

            public int y;

            public int radius;

            public int height;

        }

        private static int _BITMAP_WIDTH = 0;

        private static int _BITMAP_HEIGHT = 0;

        private static int _BITS = 4; /* Dont change this, it 24 bit bitmaps are not supported*/

#if _JAGGED_ARRAYS 

        private static int[][][] _waveHeight;

#endif

#if _RECTANGULAR_ARRAYS

        private static int[,,] _waveHeight;

#endif

#if _LINEAR_ARRAYS

        private static int[] _waveHeight;

#endif

        private static DropData[] _drops;

        private FastBitmap _p_w_picpath = null;

        private FastBitmap _originalImage = null;

        public  int _currentHeightBuffer = 0;

        public  int _newHeightBuffer = 0;

        private byte[] _bitmapOriginalBytes;

        private Random _r = new Random();

        public Main()

        {

            InitializeComponent();

        }

        private void Main_Load(object sender, EventArgs e)

        {

            this.Left = 1;

            this.Top = 6;

            //

            //

            //            

            this.pbViewport.Top = 0;

            this.pbViewport.Left = 0;

            this.pbViewport.SizeMode = PictureBoxSizeMode.AutoSize; 

            this.Width = this.pbViewport.Width;

            this.Height = this.pbViewport.Height;

            _BITMAP_WIDTH = this.pbViewport.Width;

            _BITMAP_HEIGHT = this.pbViewport.Height;            

            //

            //  

            //

#if _JAGGED_ARRAYS

            _waveHeight = new int[_BITMAP_WIDTH][][];

            for (int i = 0; i < _BITMAP_WIDTH; i++)

            {

                _waveHeight[i] = new int[_BITMAP_HEIGHT][];                

                for (int j = 0; j < _BITMAP_HEIGHT; j++)

                {

                    _waveHeight[i][j] = new int[2];

                }

            }

#endif

#if _RECTANGULAR_ARRAYS

            _waveHeight = new int[_BITMAP_WIDTH, _BITMAP_HEIGHT, 2];

#endif

#if _LINEAR_ARRAYS

            _waveHeight = new int[_BITMAP_WIDTH * _BITMAP_HEIGHT * 2];

#endif

            //

            //

            //            

            CreateBitmap();

            CreateWaterDrops();

            this.waterTime.Enabled = true;

            this.dropsTime.Interval = 50;

            this.dropsTime.Enabled = true;

        }

        private void CreateBitmap()

        {

            _originalImage = new FastBitmap((Bitmap)(this.pbViewport.Image).Clone(),_BITS);

            _originalImage.LockBits();

            _p_w_picpath = new FastBitmap((Bitmap)(this.pbViewport.Image).Clone(), _BITS);

            _bitmapOriginalBytes = new byte[_BITS * _p_w_picpath.Width() * _p_w_picpath.Height()];

            _p_w_picpath.LockBits();

            Marshal.Copy(_p_w_picpath.Data().Scan0, _bitmapOriginalBytes, 0, _bitmapOriginalBytes.Length);

            _p_w_picpath.Release();

        }

        private void DropWater(int x, int y,int radius, int height)

        {

            long _distance;

            int _x;

            int _y;            

            Single _ratio;

            _ratio = (Single)((Math.PI / (Single)radius));

            for (int i = -radius; i <= radius; i++)

            {

                for (int j = -radius; j <= radius; j++)

                {

                    _x = x + i;

                    _y = y + j;

                    if ((_x >= 0) && (_x <= _BITMAP_WIDTH - 1) && (_y >= 0) && (_y <= _BITMAP_HEIGHT - 1))

                    {

                        _distance = (long)Math.Sqrt(i * i + j * j);

                        if (_distance <= radius)

                        {

#if _JAGGED_ARRAYS

                            _waveHeight[_x][_y][_currentHeightBuffer] = (int)(height * Math.Cos((Single)_distance * _ratio));

#endif

#if _RECTANGULAR_ARRAYS

                            _waveHeight[_x,_y,_currentHeightBuffer] = (int)(height * Math.Cos((Single)_distance * _ratio));

#endif

#if _LINEAR_ARRAYS

                            _waveHeight[INDEX3D(_x, _y, _currentHeightBuffer)] = (int)(height * Math.Cos((Single)_distance * _ratio));

#endif

                        }

                    }

                }

            }

        }

        private void PaintWater()

        {

            _newHeightBuffer = (_currentHeightBuffer + 1) % 2;

            _p_w_picpath.LockBits();

#if _BUFFERED_RENDERING

            byte[] _bufferBits = new byte[_BITS * _p_w_picpath.Width() * _p_w_picpath.Height()];

            Marshal.Copy(_p_w_picpath.Data().Scan0,_bufferBits,0,_bufferBits.Length );

#endif

            //

            // 

            //

            int _offX;

            int _offY;

            for (int _x = 1; _x < _BITMAP_WIDTH - 1; _x++)

            {

                for (int _y = 1; _y < _BITMAP_HEIGHT - 1; _y++)

                {

#if _JAGGED_ARRAYS

                    //

                    //  Simulate movement.

                    //

                    unchecked

                    {

                        _waveHeight[_x][_y][_newHeightBuffer] = ((

                            _waveHeight[_x - 1][_y][_currentHeightBuffer] +

                            _waveHeight[_x - 1][_y - 1][_currentHeightBuffer] +

                            _waveHeight[_x][_y - 1][_currentHeightBuffer] +

                            _waveHeight[_x + 1][_y - 1][_currentHeightBuffer] +

                            _waveHeight[_x + 1][_y][_currentHeightBuffer] +

                            _waveHeight[_x + 1][_y + 1][_currentHeightBuffer] +

                            _waveHeight[_x][_y + 1][_currentHeightBuffer] +

                            _waveHeight[_x - 1][_y + 1][_currentHeightBuffer]) >> 2)

                        - _waveHeight[_x][_y][_newHeightBuffer];

                    }

                    //

                    //  Dampenning.

                    //

                    _waveHeight[_x][_y][_newHeightBuffer] -= (_waveHeight[_x][_y][_newHeightBuffer] >> 5);

                    //

                    //

                    //

                    _offX = ((_waveHeight[_x - 1][_y][_newHeightBuffer] - _waveHeight[_x + 1][_y][_newHeightBuffer])) >> 3;

                    _offY = ((_waveHeight[_x][_y - 1][_newHeightBuffer] - _waveHeight[_x][_y + 1][_newHeightBuffer])) >> 3;

#endif

#if _RECTANGULAR_ARRAYS

                    unchecked

                    {

                        _waveHeight[_x,_y,_newHeightBuffer] = ((

                            _waveHeight[_x - 1,_y,_currentHeightBuffer] +

                            _waveHeight[_x - 1,_y - 1,_currentHeightBuffer] +

                            _waveHeight[_x,_y - 1,_currentHeightBuffer] +

                            _waveHeight[_x + 1,_y - 1,_currentHeightBuffer] +

                            _waveHeight[_x + 1,_y,_currentHeightBuffer] +

                            _waveHeight[_x + 1,_y + 1,_currentHeightBuffer] +

                            _waveHeight[_x,_y + 1,_currentHeightBuffer] +

                            _waveHeight[_x - 1,_y + 1,_currentHeightBuffer]) >> 2)

                        - _waveHeight[_x,_y,_newHeightBuffer];

                    }

                    //

                    //  Dampenning.

                    //

                    _waveHeight[_x,_y,_newHeightBuffer] -= (_waveHeight[_x,_y,_newHeightBuffer] >> 5);

                    //

                    //

                    //

                    _offX = ((_waveHeight[_x - 1,_y,_newHeightBuffer] - _waveHeight[_x + 1,_y,_newHeightBuffer])) >> 4;

                    _offY = ((_waveHeight[_x,_y - 1,_newHeightBuffer] - _waveHeight[_x,_y + 1,_newHeightBuffer])) >> 4;

#endif

#if _LINEAR_ARRAYS

                    unchecked

                    {

                        _waveHeight[INDEX3D(_x,_y, _newHeightBuffer)] = ((

                            _waveHeight[INDEX3D(_x - 1, _y + 0, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x - 1, _y - 1, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x - 0, _y - 1, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x + 1, _y - 1, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x + 1, _y + 0, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x + 1, _y + 1, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x + 0, _y + 1, _currentHeightBuffer)] +

                            _waveHeight[INDEX3D(_x - 1, _y + 1, _currentHeightBuffer)]) >> 2)

                        - _waveHeight[INDEX3D(_x, _y, _newHeightBuffer)];

                    }

                    //

                    //  Dampenning.

                    //

                    _waveHeight[INDEX3D(_x, _y, _newHeightBuffer)] -= (_waveHeight[INDEX3D(_x, _y, _newHeightBuffer)] >> 5);

                    //

                    //

                    //

                    _offX = ((_waveHeight[INDEX3D(_x - 1, _y - 0, _newHeightBuffer)] - _waveHeight[INDEX3D(_x + 1, _y + 0, _newHeightBuffer)])) >> 4;

                    _offY = ((_waveHeight[INDEX3D(_x + 0, _y - 1, _newHeightBuffer)] - _waveHeight[INDEX3D(_x + 0, _y + 1, _newHeightBuffer)])) >> 4;

#endif

                    //

                    //  Nothing to do

                    //

                    if ((_offX == 0) && (_offY == 0)) continue;

                    //

                    //  Fix boundaries

                    //

                    if (_x + _offX <= 0) _offX = -_x;

                    if (_x + _offX >= _BITMAP_WIDTH - 1) _offX = _BITMAP_WIDTH - _x - 1;

                    if (_y + _offY <= 0) _offY = -_y;

                    if (_y + _offY >= _BITMAP_HEIGHT - 1) _offY = _BITMAP_HEIGHT - _y - 1;                    

                    //

                    //  

                    //

#if _BUFFERED_RENDERING

                    _bufferBits[_BITS * (_x + _y * _BITMAP_WIDTH) + 0] = _bitmapOriginalBytes[_BITS * (_x + _offX + (_y + _offY) * _BITMAP_WIDTH) + 0];

                    _bufferBits[_BITS * (_x + _y * _BITMAP_WIDTH) + 1] = _bitmapOriginalBytes[_BITS * (_x + _offX + (_y + _offY) * _BITMAP_WIDTH) + 1];

                    _bufferBits[_BITS * (_x + _y * _BITMAP_WIDTH) + 2] = _bitmapOriginalBytes[_BITS * (_x + _offX + (_y + _offY) * _BITMAP_WIDTH) + 2];

                    // I dont not implement the ALPHA as previous version did. you can if you want.

                    //_bufferBits[_BITS * (_x + _y * _BITMAP_WIDTH) + 3] = alpha                    

#else

                    _p_w_picpath.SetPixel(_x, _y, _originalImage.GetPixel(_x + _offX, _y + _offY));

#endif

                }

            }

#if _BUFFERED_RENDERING

            Marshal.Copy(_bufferBits,0,_p_w_picpath.Data().Scan0, _bufferBits.Length);

#endif

            _currentHeightBuffer = _newHeightBuffer;

            this.Invalidate();

        }

        private void waterTime_Tick(object sender, EventArgs e)

        {

            if (_p_w_picpath.IsLocked()) return;

            waterTime.Stop();            

            PaintWater();                        

            waterTime.Start();

        }

        private void dropsTime_Tick(object sender, EventArgs e)

        {

            this.dropsTime.Enabled = false; 

            int _percent = (int)(0.005 * (this.Width + this.Height));

            int _dropsNumber = _r.Next(_percent);

            int _drop = 0;

            for (int i = 0; i < _dropsNumber; i++)

            {             

                _drop = _r.Next(_drops.Length);                

                DropWater(_drops[_drop].x, _drops[_drop].y, _drops[_drop].radius, _drops[_drop].height);

            }

            this.dropsTime.Interval = _r.Next(15*_percent)+1;

            this.dropsTime.Enabled = true;

        }

        private void CreateWaterDrops()

        {

            int _dropX;

            int _dropY;

            int _dropRadius;

            int _height;

            int _percent = (int)(0.0015 * (this.Width + this.Height));

            _drops = new DropData[100];

            for (int i = 0; i < _drops.Length; i++)

            {

                _dropX = _r.Next(_BITMAP_WIDTH);

                _dropY = _r.Next(_BITMAP_HEIGHT);

                _height = _r.Next(400);

                _dropRadius = _r.Next(4 * _percent);

                if (_dropRadius < 4) _dropRadius = 4;

                _drops[i].x = _dropX;

                _drops[i].y = _dropY;

                _drops[i].radius = _dropRadius;

                _drops[i].height = _height;

            }

        }

        private void pbViewport_Click(object sender, EventArgs e)

        {

            this.Close();

        }

        private void Main_Click(object sender, EventArgs e)

        {

            this.Close();

        }

        private void Main_Paint(object sender, PaintEventArgs e)

        {            

            _p_w_picpath.Release();           

            e.Graphics.DrawImage(_p_w_picpath.Bitmap, 0, 0, _p_w_picpath.Width(), _p_w_picpath.Height());           

        }

#if _LINEAR_ARRAYS

        private int INDEX3D(int x, int y, int z) { unchecked { return x * _BITMAP_HEIGHT * 2 + y * 2 + z; } }

#endif

    }

}

program.cs

using System;

using System.Collections.Generic;

using System.Windows.Forms;

//Download by http://www.codefans.net

namespace WaveEffects

{

    static class Program

    {

        /// <summary>

        /// The main entry point for the application.

        /// </summary>

        [STAThread]

        static void Main()

        {

            Application.EnableVisualStyles();

            Application.SetCompatibleTextRenderingDefault(false);

            Application.Run(new Main());

        }

    }

}