C#:图片的 粒子化 破碎效果

0.

之前是做java语言安卓开发,看到了图片的粒子化破碎效果,一直没时间好好研究。

这次在c#语言中做窗体应用开发,终于研究出这个效果了。

文章是借鉴Android的,不过原理都差不多。

学习网址如下:

https://www.jianshu.com/p/12184d861646

1. 

先看看图片的像素级操作的代码,很简单

//初识
//创建一个2X2的图片,每个像素占24位,rgb各通道占一个字节
        Bitmap cbmp = new Bitmap(2, 2, PixelFormat.Format24bppRgb);
        //给每个像素上色
        cbmp.SetPixel(0, 0, Color.Black); 
        cbmp.SetPixel(1, 0, Color.Red);
        cbmp.SetPixel(0, 1, Color.White);
        cbmp.SetPixel(1, 1, Color.Blue);
        //保存图片,其实图片就是很多个像素的组成,每个像素是一个颜色
        cbmp.Save(@"G:\新建文件夹\cbmp.png", ImageFormat.Png);
        //得到图片某个像素的颜色
        Color pixel_0_0 = cbmp.GetPixel(0, 0);
        Color pixel_1_0 = cbmp.GetPixel(1, 0);
        Color pixel_0_1 = cbmp.GetPixel(0, 1);
        Color pixel_1_1 = cbmp.GetPixel(1, 1);
        //得到颜色各通道的值
        int r = pixel_0_0.R;
        int g = pixel_0_0.G;
        int b = pixel_0_0.B;
        //全部像素
        Color[,] colorArray = new Color[height, width];
        for (int j = 0; j<height; j++) {
            for (int i = 0; i<width; i++) {
                colorArray[j, i] = bmp.GetPixel(i, j);
            }

2. 

第一次尝试粒子化 图片   (2是失败,成功见3

既然是对像素级粒子操作,是不是应该定义一个粒子对象。

Ball.cs

class Ball{
        public float x;//点位X
        public float y;//点位Y
        public Color color;//颜色
}

获取原图的 粒子集合 如下:

// d 代表每个粒子的大小  
//把所有粒子组成图片,则大小为原图bitmap的  (bitmap.width*d ,  bitmap.height*d)

private List<Ball> initBall(Bitmap bitmap) {
    List<Ball> mBalls = new List<Ball>();//粒子集合
    for (int i = 0; i < bitmap.Width; i++) { //一列列的来
        for (int j = 0; j < bitmap.Height; j++) {
            Ball ball = new Ball();
            ball.x = i * d + d / 2;
            ball.y = j * d + d / 2;
            ball.color = bitmap.GetPixel(i, j); //一定要一列列的来。一行行的来,图片会旋转
            mBalls.Add(ball);
        }
    }
    return mBalls;
}

这样我们就有了一个图片的所有粒子,那么所有的操作,你都可以做了。

我们只要定一个定时器,不断的改变的每个粒子的位置,定时器中不断的重绘PictureBox就可以了。

绘制方法如下: (可以放到 PictureBox 的 OnPaint()方法中重绘)

//gp 是Graphics

private void dwawBalls(List<Ball> mBalls) {

    foreach (Ball ball in mBalls) {
        gp.DrawEllipse(new Pen(ball.color), new Rectangle((int)(ball.x - d / 2), (int)(ball.y - d / 2), d, d));
        //gp.DrawRectangle(new Pen(ball.color) , new Rectangle((int)(ball.x - d / 2), (int)(ball.y - d / 2) ,  d, d)); //矩形
    //可自定义其它图形代表粒子

    }

}

理论上上面的代码已经解决所有问题,但是实际上,用上面的方法似乎并不能达到想要的效果。

第一,粒子组成图片背景会变化,对于有一些图片会变的很暗。

第二,粒子组成图片尺寸改变了,变成了原图的 d 倍。

第三,不利于绘制在固定大小的pictureBox控件中

所以我想到了下面的另一种达到粒子化效果的思路。

3.

因为在上面0步提供的学习网址中,似乎它的图片粒子化后组成和原图一样大,可以显示在控件中,且可以变化。那如果我用2中的方法是绝无可能的,因为粒子有大小,再组成图片肯定尺寸会变大 d 倍。

所以我不想用像素级去解决粒子化效果,就很普通的思考就有以下结果:

我们可以把一张图片分成很多个小块,不就行了吗。

当小块很多的时候,就达到了伪像素级

所以我们定义每个小块对象如下:

MyBitmap.cs

using System.Drawing;

namespace DiyBmp {
    class MyBitmap {
        public Bitmap bmp;
        public Point point;
        public int size;
    }
}

界面内容介绍如下:

  • 一个 PictureBox控件画图,占满窗体。
  • 3个按钮(选图 , 开启粒子破碎  ,  停止破碎)
  • 一个定时器 timer1

逻辑代码如下:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

namespace DiyBmp {
    //在以下注释中 粒子和块表示同一个意思
    public partial class Form1 : Form {

        int pw;    //pictureBox显示大小
        int ph;

        Graphics gp; //pictureBox显示的画板

        int firstx = 200; //初始位置
        int firsty = 100;

        int dv = 100;     //行 个数(粒子密度)
        int dsize = 0;   //根据行 个数计算出来的  块的大小(粒子的大小)
        List<MyBitmap> bs = new List<MyBitmap>(); //粒子集合
        
        public Form1() {
            InitializeComponent();

            pw = this.pictureBox1.Width;
            ph = this.pictureBox1.Height;
        }

        //选图
        private void button1_Click(object sender, EventArgs e) {
            Bitmap exampleBmp = new Bitmap(200,200);
            //不管原图多大,缩放原图大小为200,200
            Bitmap sourceBmp = new Bitmap(@"G:\新建文件夹\my.png");
            Graphics.FromImage(exampleBmp).DrawImage(sourceBmp,0,0,200,200); //图片缩放
            exampleBmp.Save(@"G:\新建文件夹\my_200.png");
            dsize = exampleBmp.Width / dv;

            //在 (firstx,firsty)显示200X200的图
            gp = this.pictureBox1.CreateGraphics();
            gp.Clear(Color.White);
            gp.DrawImage(exampleBmp , new Point(firstx , firsty));

            //获取所有块
            bs.Clear();
            for (int  x = 0; x< exampleBmp.Width; x+=dsize) {
                for (int y = 0; y< exampleBmp.Height; y+=dsize) {
                    MyBitmap mb = new MyBitmap();
                    Bitmap oneb = new Bitmap(dsize,dsize);
                    Graphics.FromImage(oneb).Clear(Color.White);
                    //依次截取部分区域存储 从(原图exampleBmp  x,y处截取dsize大小的图)给oneb
                    Graphics.FromImage(oneb).DrawImage(exampleBmp ,0 , 0, new Rectangle(x,y,dsize,dsize) , GraphicsUnit.Pixel);//图片部分截取
                    mb.bmp = oneb;
                    mb.size = dsize;
                    mb.point = new Point(x+firstx,y+firsty); //修改在画板中的坐标值(如果不让其默认在左上角)
                    bs.Add(mb);
                }
            }
            //在 (firstx,firsty)显示200X200的图 的中心位置,决定粒子散开的路径
            cx = (firstx + dv / 2 * dsize);
            cy = (firsty + dv /2 * dsize);
            
        }
        
        //一秒定时器触发事件
        private void timer1_Tick(object sender, EventArgs e) {
            if (bs.Count == 0) {
                this.timer1.Enabled = false;
            }
            //更新粒子位置
            UpdateMyBitmap(bs);
            this.pictureBox1.Invalidate(); //使用重绘,达到粒子体验
            //drawMyBitmap(bs);  //该方法显示不连续,毫无粒子体验。
        }

        //gp = this.pictureBox1.CreateGraphics();   //该方法毫无粒子体验
        private void drawMyBitmap(List<MyBitmap> bs) {
            gp.Clear(Color.White); //清除上一次位置
            for (int i = 0; i < bs.Count; i++) {
                gp.DrawImage(bs[i].bmp , bs[i].point);
            }
        }
        
        int cx;
        int cy;
        long starttime = 0;
        Random m = new Random();
        private void UpdateMyBitmap(List<MyBitmap> b) {
            for (int i = 0 ; i < b.Count; i++) {
                MyBitmap mybtmp = b[i];
                if (DateTime.Now.Ticks/1000 - starttime/1000 > 8000)
                    b.RemoveAt(i);

                int x = mybtmp.point.X;
                int y = mybtmp.point.Y;

                //根据中心,向四个象限散去
                if (x>cx && y < cy) {
                    x = x + 5 + m.Next(-20,20);
                    y = y - 5 + m.Next(-20, 20);
                } else if (x<cx && y<cy) {
                    x = x - 5 + m.Next(-20, 20);
                    y = y - 5 + m.Next(-20, 20);
                } else if (x < cx && y > cy) {
                    x = x - 5 + m.Next(-20, 20);
                    y = y + 5 + m.Next(-20, 20);
                }
                else {
                    x = x + 5 + m.Next(-20, 20);
                    y = y + 5 + m.Next(-20, 20);
                }
                mybtmp.point = new Point(x,y);
            }
        }

        //开启粒子效果 定时
        private void button2_Click(object sender, EventArgs e) {
            starttime = DateTime.Now.Ticks;
            timer1.Enabled = true;
            timer1.Interval = 100;
        }

        //关闭粒子效果 定时
        private void button3_Click(object sender, EventArgs e) {
            timer1.Enabled = false;
        }
        
        //重绘
        private void pictureBox1_Paint(object sender, PaintEventArgs e) {
            //e.Graphics.Clear(Color.White); //清除上一次位置 。重绘已经清除了上次的
            e.Graphics.Clear(Color.White);
            for (int i = 0; i < bs.Count; i++) {
                e.Graphics.DrawImage(bs[i].bmp, bs[i].point);
            }
        }
    }
}

效果如下:

完!

猜你喜欢

转载自blog.csdn.net/qq_38261174/article/details/86157140