使用C#生成Excel像素画

效果预览:

        设计思路:通过设置每个单元格的背景色,利用每个单元格做像素点作画

        使用工具:C# with NPOI

一、界面

        简单的界面,包括图片选择按钮、图片预览控件、Excel生成按钮,用户可以自行设置需要生成的行数和列数。

二、实现方法

        读取图片至Bitmap → 生成缩略图 → 创建Workbook工作簿并填充单元格

        代码如下:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Forms;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

namespace HWPicture2Excel
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        Bitmap bmp = null;

        /// <summary>
        /// 更新数目
        /// </summary>
        object lock1 = new object();
        /// <summary>
        /// 更新线程数
        /// </summary>
        object lock2 = new object();
        /// <summary>
        /// 更新字典
        /// </summary>
        object lock3 = new object();

        private void buttonOpenPicture_Click(object sender, EventArgs e)
        {
            if (isCreating)
            {
                MessageBox.Show("正在生成中,请稍等");
                return;
            }

            OpenFileDialog ofd = new OpenFileDialog();

            ofd.Filter = "|*.jpg||*.png||*.bmp";

            if (ofd.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            try
            {
                bmp = new Bitmap(ofd.FileName);
            }
            catch
            {
                MessageBox.Show("图片已损坏!");
                return;
            }

            pictureBox.Image = bmp;

            labelSize.Text = bmp.Width + "×" + bmp.Height;
        }

        private void btnCreate_Click(object sender, EventArgs e)
        {
            if (bmp == null)
            {
                MessageBox.Show("先读取图片!");
                return;
            }

            if (isCreating)
            {
                MessageBox.Show("正在生成中,请稍等");
                return;
            }

            SaveFileDialog sfd = new SaveFileDialog();

            sfd.Filter = "|*.xlsx";

            if (sfd.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            //缓存选择的路径
            path = sfd.FileName;

            CreatExcel();
        }

        string path;
        bool isCreating = false;

        private void CreatExcel()
        {
            isCreating = true;

            int width = 100;
            int.TryParse(textColunm.Text, out width);
            int height = 100;
            int.TryParse(textRow.Text, out height);

            if (width > 16384)
            {
                MessageBox.Show("超过最大列数");
                return;
            }
            if (height > 1048576)
            {
                MessageBox.Show("超过最大行数");
                return;
            }


            //生成缩略图
            Bitmap newBmp = new Bitmap(width, height);
            using (Graphics g = Graphics.FromImage(newBmp))
            {
                g.InterpolationMode = InterpolationMode.HighQualityBicubic;
                g.DrawImage(bmp, 0, 0, width, height);
            }
            int total = width * height;

            labelTotal.Text = "总数:" + total.ToString();

            XSSFWorkbook workbook = new XSSFWorkbook();
            ISheet sheet1 = workbook.CreateSheet("Sheet1");

            int count = 0;
            int threadCount = 0;

            //对XSSFCellStyle做缓存
            Dictionary<Color, XSSFCellStyle> colorDic = new Dictionary<Color, XSSFCellStyle>();

            for (int i = 0; i < height; i++)
            {
                if (sheet1.GetRow(i) == null) sheet1.CreateRow(i);

                for (int j = 0; j < width; j++)
                {
                    if (i == 0)
                    {
                        sheet1.SetColumnWidth(j, 450);
                    }

                    if (j == 0)
                    {
                        sheet1.GetRow(i).Height = 200;
                    }

                    ICell cell = sheet1.GetRow(i).CreateCell(j);
                    Color color = newBmp.GetPixel(j, i);
                    color = Color.FromArgb(color.R, color.G, color.B);       //舍弃A通道

                    if (!(color.R == 255 && color.G == 255 && color.B == 255))  //实测若是纯白会变黑色,舍之
                    {

                        Task t = new Task(() =>
                        {
                            //显示线程数
                            int d;

                            lock (lock2)
                            {
                                threadCount++;
                            }
                            d = threadCount;

                            delegate4UI d1 = () =>
                            {
                                labelThreadCount.Text = "线程:" + d.ToString();
                            };

                            BeginInvoke(d1);

                            bool isGet;
                            XSSFCellStyle style;

                            lock (lock3)
                            {
                                isGet = colorDic.TryGetValue(color, out style);

                                if (!isGet)
                                {
                                    style = workbook.CreateCellStyle() as XSSFCellStyle;
                                    colorDic.Add(color, style);
                                }
                            }

                            if (!isGet)
                            {
                                XSSFColor xColor = workbook.CreateColor(color);

                                style.SetFillForegroundColor(xColor);     //这行代码是最耗时的一步
                                style.FillPattern = FillPattern.SolidForeground;
                            }

                            cell.CellStyle = style;

                            lock (lock1)
                            {
                                count++;
                            }


                            int c = count;


                            lock (lock2)
                            {
                                threadCount--;
                            }

                            d = threadCount;
                            delegate4UI d2 = () =>
                            {
                                if (c == total)
                                {
                                    WriteToExcel(workbook);
                                    labelProgress.Text = "完成!";
                                }
                                else
                                {
                                    labelFinish.Text = "完成:" + c;
                                    labelProgress.Text = "进度:" + (100.0f * c / total).ToString("0.00") + "%";
                                }
                                labelThreadCount.Text = "线程:" + d.ToString();

                            };

                            BeginInvoke(d2);

                        });

                        t.Start();

                    }
                    else
                    {
                        lock (lock1)
                        {
                            count++;
                        }

                        int c = count;

                        labelFinish.Text = "完成:" + c;
                        labelProgress.Text = "进度:" + (100.0f * c / total).ToString("0.00") + "%";

                        if (c == total)
                        {
                            WriteToExcel(workbook);
                            labelProgress.Text = "完成!";
                        }
                    }
                }
            }

        }

        /// <summary>
        /// 最后的写入
        /// </summary>
        /// <param name="workbook"></param>
        private void WriteToExcel(XSSFWorkbook workbook)
        {
            using (FileStream fs = new FileStream(path, FileMode.Create))
            {
                //将内存里面的文档对象,写入到一个文档流中
                workbook.Write(fs);
            }

            isCreating = false;
        }

        /// <summary>
        /// 单纯为了更新UI定义的delegate
        /// </summary>
        public delegate void delegate4UI();

        #region 限制文本输入
        private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
        {

            if (textRow.Text == string.Empty)
            {
                if (e.KeyChar <= '0' || e.KeyChar > '9') e.Handled = true;
            }
            else if ((e.KeyChar < '0' || e.KeyChar > '9') && e.KeyChar != 8)
            {
                e.Handled = true;
            }
        }

        private void textBox2_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (textColunm.Text == string.Empty)
            {
                if (e.KeyChar <= '0' || e.KeyChar > '9') e.Handled = true;
            }
            else if ((e.KeyChar < '0' || e.KeyChar > '9') && e.KeyChar != 8)
            {
                e.Handled = true;
            }
        }
        #endregion
    }

}

        两个扩展方法,用于通过RGB生成XSSFColor

using NPOI.XSSF.UserModel;
using System.Drawing;

namespace HWPicture2Excel
{
    public static class Extension
    {
        public static XSSFColor CreateColor(this XSSFWorkbook workbook, Color color)
        {
            return workbook.CreatColor(color.R, color.G, color.B);
        }

        public static XSSFColor CreatColor(this XSSFWorkbook workbook, byte r, byte g, byte b)
        {
            XSSFColor aColor = new XSSFColor();
            byte[] by = { r, g, b };
            aColor.SetRgb(by);

            return aColor;
        }
    }
}

三、注意的地方

       1.可能是NPOI本身的问题,越到后面速率越慢,240*320的测试图都够跑几个小时(orz),再大基本就GG了。

       2.NPOI里【XSSFCellStyle】中的【public void SetFillForegroundColor(XSSFColor color)】方法耗时相当可怕,故代码中对XSSFCellStyle做了缓存处理,如果是单一颜色的图片基本是秒生成,但对于颜色较为复杂的图片用处不大。

       3.未知原因,RGB为 255,255,255 (纯白)的单元格在生成后会变成黑色,故对于此类单元格直接作不填充处理。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值