ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 2377|回复: 12

[原创] 有感于Excel手工作画之VBA自动绘画

[复制链接]

TA的精华主题

TA的得分主题

发表于 2022-6-1 15:14 | 显示全部楼层 |阅读模式
本帖最后由 /pig流云 于 2022-6-2 08:35 编辑

       最近看到一篇文章讲述了一个日本老人(链接:日本69岁老者用Excel作画 一幅画完成要1到3天 _ 游民星空 GamerSky.com)使用Excel来作画,在佩服老人的毅力,同时也思考了如果直接用VBA来作画呢。

        从网络上检索了一些信息,有用的信息并不算太多,静心思考后决定直接从文件格式来下手,就是直接解析BMP文件格式。
        BMP文件格式,又称为Bitmap(位图)或是DIB(Device-Independent Device,设备无关位图),是Windows系统中广泛使用的图像文件格式。文件中详细的记录了每个像素的RGB值,这样只要能获取到这个信息,那就万事大吉了。
        BMP文件的头部共54个字节,分成两部分即文件头信息和位图信息头。另外的两部分就是调色盘信息及数据部分。具体可以百度,此处不再赘述。下面就跟着我一步步来学习如何绘画。
        一、定义结构类型
  1. '位图文件头包含有关于文件类型、文件大小、存放位置等信息
  2. Type BITMAPFILEHEADER
  3.     bfType As Integer
  4.     bfSize As Long
  5.     bfReserved1 As Integer
  6.     bfReserved2 As Integer
  7.     bfOffBits As Long
  8. End Type

  9. '调色盘信息
  10. Type RGBQUAD
  11.     rgbBlue As Byte
  12.     rgbGreen As Byte
  13.     rgbRed As Byte
  14.     rgbReserved As Byte
  15. End Type

  16. '位图内容结构信息记录文件高、宽、位、调色盘大小等信息
  17. Type BITMAPINFOHEADER '40 bytes
  18.     biSize As Long
  19.     biWidth As Long
  20.     biHeight As Long
  21.     biPlanes As Integer
  22.     biBitCount As Integer
  23.     biCompression As Long
  24.     biSizeImage As Long
  25.     biXPelsPerMeter As Long
  26.     biYPelsPerMeter As Long
  27.     biClrUsed As Long
  28.     biClrImportant As Long
  29. End Type

  30. '像素点分量
  31. Type RGBTRIPLE
  32.     rgbtBlue As Byte
  33.     rgbtGreen As Byte
  34.     rgbtRed As Byte
  35. End Type
复制代码
       二、读取位图的信息及数据的函数

  1. Sub readBmp()
  2.     On Error GoTo errClose
  3.     Dim filePath As String
  4.     Dim bmpHeaer As BITMAPFILEHEADER
  5.     Dim bmpBi As BITMAPINFOHEADER
  6.     Dim rgb As RGBTRIPLE, bit8 As Byte, bit16 As Integer, bit16RGB As Integer
  7.     Dim i As Long, j As Long
  8.     Dim OK As Boolean
  9.     filePath = [E21]
  10.     OK = False
  11.     'Open filePath For Binary Access Write As #1
  12.     Open filePath For Binary As #1
  13.     'Debug.Print (Seek(1) - 1)
  14.     Seek 1, 1 '设置读入点为起始
  15.     '读取头文件
  16.     Get #1, , bmpHeaer    '读取BMP文件头

  17.     [b2] = "'" & Hex(bmpHeaer.bfType)     'bfType:
  18.     [b3] = "'" & Hex(bmpHeaer.bfSize)       'bfSize:
  19.     [b4] = "'" & Hex(bmpHeaer.bfReserved1)       'bfReserved1:
  20.     [b5] = "'" & Hex(bmpHeaer.bfReserved2)       'bfReserved2:
  21.     [b6] = "'" & Hex(bmpHeaer.bfOffBits)      'bfOffBits:
  22.    
  23.     [c2] = "'" & (bmpHeaer.bfType)     'bfType:
  24.     [c3] = "'" & (bmpHeaer.bfSize)       'bfSize:
  25.     [c4] = "'" & (bmpHeaer.bfReserved1)       'bfReserved1:
  26.     [c5] = "'" & (bmpHeaer.bfReserved2)       'bfReserved2:
  27.     [c6] = "'" & (bmpHeaer.bfOffBits)      'bfOffBits:
  28.    
  29.     '读取位图信息
  30.     Get #1, , bmpBi
  31.    
  32.     [B8] = Hex(bmpBi.biSize)
  33.     [B9] = Hex(bmpBi.biWidth)
  34.     [B10] = Hex(bmpBi.biHeight)
  35.     [B11] = Hex(bmpBi.biPlanes)
  36.     [B12] = Hex(bmpBi.biBitCount)
  37.     [B13] = Hex(bmpBi.biCompression)
  38.     [B14] = Hex(bmpBi.biSizeImage)
  39.     [B15] = Hex(bmpBi.biXPelsPerMeter)
  40.     [B16] = Hex(bmpBi.biYPelsPerMeter)
  41.     [B17] = Hex(bmpBi.biClrUsed)
  42.     [B18] = Hex(bmpBi.biClrImportant)
  43.    
  44.    
  45.     [C8] = (bmpBi.biSize)
  46.     [C9] = (bmpBi.biWidth)
  47.     [C10] = (bmpBi.biHeight)
  48.     [C11] = (bmpBi.biPlanes)
  49.     [C12] = (bmpBi.biBitCount)
  50.     [C13] = (bmpBi.biCompression)
  51.     [C14] = (bmpBi.biSizeImage)
  52.     [C15] = (bmpBi.biXPelsPerMeter)
  53.     [C16] = (bmpBi.biYPelsPerMeter)
  54.     [C17] = (bmpBi.biClrUsed)
  55.     [C18] = (bmpBi.biClrImportant)
  56.    
  57.    
  58.     '清除历史数据
  59.     Sheet2.Cells.Clear
  60.     Application.ScreenUpdating = False
  61.     Select Case bmpBi.biBitCount
  62.         Case 8 '处理8位位图
  63.             Dim RGBQUADS() As RGBQUAD
  64.             ReDim RGBQUADS(bmpBi.biClrUsed)
  65.             For i = LBound(RGBQUADS) To UBound(RGBQUADS)
  66.                 Get #1, , RGBQUADS(i)  '读取所有调色盘预定义值
  67.             Next
  68.             Seek 1, bmpHeaer.bfOffBits + 1 '跳转到数据区
  69.             For i = 1 To bmpBi.biHeight
  70.                 For j = 1 To bmpBi.biWidth
  71.                     Get #1, , bit8  '读取RGB数据,8位用一个字节表示,对应0-255值,RGB值由调色盘预定义
  72.                     Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = VBA.rgb(RGBQUADS(bit8).rgbRed, RGBQUADS(bit8).rgbGreen, RGBQUADS(bit8).rgbBlue)
  73.                 Next
  74.                 DoEvents
  75.                 Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
  76.             Next
  77.         Case 16 '处理16位位图
  78.             Seek 1, bmpHeaer.bfOffBits + 1
  79.             For i = 1 To bmpBi.biHeight
  80.                 For j = 1 To bmpBi.biWidth
  81.                     Get #1, , bit16 '读取RGB信息,16位位图RGB信息由2个字节16位构成
  82.                     '低5位表示Blue分量;中5为表示Green分量;高5位表示Red分量,最高位无用
  83.                     Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = Conv16BitRGB(bit16)
  84.                 Next
  85.                 DoEvents
  86.                 Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
  87.             Next
  88.         Case 24 '处理24位位图
  89.             Seek 1, bmpHeaer.bfOffBits + 1
  90.             For i = 1 To bmpBi.biHeight
  91.                 For j = 1 To bmpBi.biWidth
  92.                     Get #1, , rgb '24位位图处理是最简单的,因为是真彩图,所以直接就是3个字节记录了RGB信息
  93.                     Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = VBA.rgb(rgb.rgbtRed, rgb.rgbtGreen, rgb.rgbtBlue)
  94.                 Next
  95.                 DoEvents
  96.                 Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
  97.             Next
  98.         Case Else
  99.             Err.Raise 4000, "", "不支持" & bmpBi.biBitCount & "位的图片格式!"
  100.     End Select
  101.     Application.StatusBar = "就绪"
  102.     OK = True
  103. errClose:
  104.     If Not OK Then
  105.         MsgBox Err.Description
  106.     Else
  107.         MsgBox "完成!"
  108.         Sheet2.Activate
  109.         [a1].Select
  110.     End If
  111.     Application.ScreenUpdating = True
  112.     Close #1
  113. End Sub

复制代码
       三、8位位图的转换函数,因为8位位图处理起来比较麻烦,需要做转换,所以单独写了一个函数来处理

  1. Function Conv16BitRGB(ByVal num As Long) As Long
  2.     Dim bit16 As Long
  3.     Dim r As Byte, g As Byte, b As Byte, r1 As Byte, g1 As Byte, b1 As Byte
  4.     '低5位表示Blue分量;中5为表示Green分量;高5位表示Red分量
  5.     '16位数据的组成例如以下
  6.     '第一个字节: g5g4g3b7b6b5b4b3
  7.     '第二个字节:0r7r6r5r4r3g7g6
  8.     '当中第二个字节的左边第一位为填充位?我在实验中用0填充?
  9.     '该16位bmp图像无调色板数据
  10.     '该16位bmp图像在显示时。图片浏览软件(如windows绘图)会将rgb555自己主动转换为rgb888显示。详细的方法例如以下
  11.     'b7b6b5b4b3->b7b6b5b4b3b7b6b5
  12.     'r7r6r5r4r3->r7r6r5r4r7r6r5
  13.     'g7g6g5g4g3->r7r6r5r4g7g6g5
  14.     bit16 = num
  15.     Call BitPlus.SHL(bit16, 17)
  16.     Call BitPlus.SHR(bit16, 27)
  17.     r = bit16
  18.     r1 = bit16
  19.     Call SHR(r1, 2)
  20.     Call SHL(r, 3)
  21.    
  22.     r = r + r1
  23.    
  24.     bit16 = num
  25.     Call BitPlus.SHL(bit16, 22)
  26.     Call BitPlus.SHR(bit16, 27)
  27.     g = bit16
  28.     g1 = bit16
  29.     Call SHR(g1, 2)
  30.     Call SHL(g, 3)
  31.     g = g + g1
  32.    
  33.     bit16 = num
  34.     Call BitPlus.SHL(bit16, 27)
  35.     Call BitPlus.SHR(bit16, 27)
  36.     b = bit16
  37.     b1 = bit16
  38.     Call SHR(b1, 2)
  39.     Call SHL(b, 3)
  40.     b = b + b1
  41.     'Debug.Print Bin(r), Bin(g), Bin(b)
  42.     Conv16BitRGB = VBA.rgb(r, g, b)
  43. End Function


复制代码


        至此代码部分就算完成了,下面展示一下效果图
20220601150020.png

20220601150529.png


完整的附件在此: vba提取bmp像素点图像信息并绘画.zip (922.86 KB, 下载次数: 98)

评分

2

查看全部评分

TA的精华主题

TA的得分主题

发表于 2022-6-1 18:10 | 显示全部楼层
未见效果图和附件?

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-6-1 20:18 来自手机 | 显示全部楼层
蓝桥玄霜 发表于 2022-6-1 18:10
未见效果图和附件?

明明传了啊!明天补上。

TA的精华主题

TA的得分主题

发表于 2022-6-5 23:18 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2022-6-6 08:03 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
这个不是绘图,这是提取像素点,没意思。

发挥表格的优势,画出失量图才有意义啊。
或者把表格作为数据输入器,调用别的程序来画图,那也好玩啊。

TA的精华主题

TA的得分主题

发表于 2022-6-6 12:39 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-6-6 14:43 | 显示全部楼层
lilyhcn1 发表于 2022-6-6 08:03
这个不是绘图,这是提取像素点,没意思。

发挥表格的优势,画出失量图才有意义啊。

你有认真看我写这个文章的意图吗,我也只是练练手而已。目的达到也练了手这就行了。

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-6-6 14:44 | 显示全部楼层
qlmgu 发表于 2022-6-6 12:39
请问楼主,测试提示路径不对,反复拷贝路径了

代码没有问题,反复测试过。

TA的精华主题

TA的得分主题

 楼主| 发表于 2022-6-6 14:45 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
yzxfelix 发表于 2022-6-5 23:18
大师啊,真正的大神在这里啊

谢谢肯定,大神就不敢当了。

TA的精华主题

TA的得分主题

发表于 2022-6-6 14:55 | 显示全部楼层
/pig流云 发表于 2022-6-6 14:43
你有认真看我写这个文章的意图吗,我也只是练练手而已。目的达到也练了手这就行了。

不打扰你研究了,愿你早日能达到老人的绘图水平。
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-5-11 18:42 , Processed in 0.041441 second(s), 11 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表