|
本帖最后由 /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个字节,分成两部分即文件头信息和位图信息头。另外的两部分就是调色盘信息及数据部分。具体可以百度,此处不再赘述。下面就跟着我一步步来学习如何绘画。
一、定义结构类型
- '位图文件头包含有关于文件类型、文件大小、存放位置等信息
- Type BITMAPFILEHEADER
- bfType As Integer
- bfSize As Long
- bfReserved1 As Integer
- bfReserved2 As Integer
- bfOffBits As Long
- End Type
- '调色盘信息
- Type RGBQUAD
- rgbBlue As Byte
- rgbGreen As Byte
- rgbRed As Byte
- rgbReserved As Byte
- End Type
- '位图内容结构信息记录文件高、宽、位、调色盘大小等信息
- Type BITMAPINFOHEADER '40 bytes
- biSize As Long
- biWidth As Long
- biHeight As Long
- biPlanes As Integer
- biBitCount As Integer
- biCompression As Long
- biSizeImage As Long
- biXPelsPerMeter As Long
- biYPelsPerMeter As Long
- biClrUsed As Long
- biClrImportant As Long
- End Type
- '像素点分量
- Type RGBTRIPLE
- rgbtBlue As Byte
- rgbtGreen As Byte
- rgbtRed As Byte
- End Type
复制代码 二、读取位图的信息及数据的函数
- Sub readBmp()
- On Error GoTo errClose
- Dim filePath As String
- Dim bmpHeaer As BITMAPFILEHEADER
- Dim bmpBi As BITMAPINFOHEADER
- Dim rgb As RGBTRIPLE, bit8 As Byte, bit16 As Integer, bit16RGB As Integer
- Dim i As Long, j As Long
- Dim OK As Boolean
- filePath = [E21]
- OK = False
- 'Open filePath For Binary Access Write As #1
- Open filePath For Binary As #1
- 'Debug.Print (Seek(1) - 1)
- Seek 1, 1 '设置读入点为起始
- '读取头文件
- Get #1, , bmpHeaer '读取BMP文件头
- [b2] = "'" & Hex(bmpHeaer.bfType) 'bfType:
- [b3] = "'" & Hex(bmpHeaer.bfSize) 'bfSize:
- [b4] = "'" & Hex(bmpHeaer.bfReserved1) 'bfReserved1:
- [b5] = "'" & Hex(bmpHeaer.bfReserved2) 'bfReserved2:
- [b6] = "'" & Hex(bmpHeaer.bfOffBits) 'bfOffBits:
-
- [c2] = "'" & (bmpHeaer.bfType) 'bfType:
- [c3] = "'" & (bmpHeaer.bfSize) 'bfSize:
- [c4] = "'" & (bmpHeaer.bfReserved1) 'bfReserved1:
- [c5] = "'" & (bmpHeaer.bfReserved2) 'bfReserved2:
- [c6] = "'" & (bmpHeaer.bfOffBits) 'bfOffBits:
-
- '读取位图信息
- Get #1, , bmpBi
-
- [B8] = Hex(bmpBi.biSize)
- [B9] = Hex(bmpBi.biWidth)
- [B10] = Hex(bmpBi.biHeight)
- [B11] = Hex(bmpBi.biPlanes)
- [B12] = Hex(bmpBi.biBitCount)
- [B13] = Hex(bmpBi.biCompression)
- [B14] = Hex(bmpBi.biSizeImage)
- [B15] = Hex(bmpBi.biXPelsPerMeter)
- [B16] = Hex(bmpBi.biYPelsPerMeter)
- [B17] = Hex(bmpBi.biClrUsed)
- [B18] = Hex(bmpBi.biClrImportant)
-
-
- [C8] = (bmpBi.biSize)
- [C9] = (bmpBi.biWidth)
- [C10] = (bmpBi.biHeight)
- [C11] = (bmpBi.biPlanes)
- [C12] = (bmpBi.biBitCount)
- [C13] = (bmpBi.biCompression)
- [C14] = (bmpBi.biSizeImage)
- [C15] = (bmpBi.biXPelsPerMeter)
- [C16] = (bmpBi.biYPelsPerMeter)
- [C17] = (bmpBi.biClrUsed)
- [C18] = (bmpBi.biClrImportant)
-
-
- '清除历史数据
- Sheet2.Cells.Clear
- Application.ScreenUpdating = False
- Select Case bmpBi.biBitCount
- Case 8 '处理8位位图
- Dim RGBQUADS() As RGBQUAD
- ReDim RGBQUADS(bmpBi.biClrUsed)
- For i = LBound(RGBQUADS) To UBound(RGBQUADS)
- Get #1, , RGBQUADS(i) '读取所有调色盘预定义值
- Next
- Seek 1, bmpHeaer.bfOffBits + 1 '跳转到数据区
- For i = 1 To bmpBi.biHeight
- For j = 1 To bmpBi.biWidth
- Get #1, , bit8 '读取RGB数据,8位用一个字节表示,对应0-255值,RGB值由调色盘预定义
- Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = VBA.rgb(RGBQUADS(bit8).rgbRed, RGBQUADS(bit8).rgbGreen, RGBQUADS(bit8).rgbBlue)
- Next
- DoEvents
- Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
- Next
- Case 16 '处理16位位图
- Seek 1, bmpHeaer.bfOffBits + 1
- For i = 1 To bmpBi.biHeight
- For j = 1 To bmpBi.biWidth
- Get #1, , bit16 '读取RGB信息,16位位图RGB信息由2个字节16位构成
- '低5位表示Blue分量;中5为表示Green分量;高5位表示Red分量,最高位无用
- Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = Conv16BitRGB(bit16)
- Next
- DoEvents
- Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
- Next
- Case 24 '处理24位位图
- Seek 1, bmpHeaer.bfOffBits + 1
- For i = 1 To bmpBi.biHeight
- For j = 1 To bmpBi.biWidth
- Get #1, , rgb '24位位图处理是最简单的,因为是真彩图,所以直接就是3个字节记录了RGB信息
- Sheet2.Cells(bmpBi.biHeight - i + 1, j).Interior.Color = VBA.rgb(rgb.rgbtRed, rgb.rgbtGreen, rgb.rgbtBlue)
- Next
- DoEvents
- Application.StatusBar = "进度" & Int(i / bmpBi.biHeight * 100) & "%"
- Next
- Case Else
- Err.Raise 4000, "", "不支持" & bmpBi.biBitCount & "位的图片格式!"
- End Select
- Application.StatusBar = "就绪"
- OK = True
- errClose:
- If Not OK Then
- MsgBox Err.Description
- Else
- MsgBox "完成!"
- Sheet2.Activate
- [a1].Select
- End If
- Application.ScreenUpdating = True
- Close #1
- End Sub
复制代码 三、8位位图的转换函数,因为8位位图处理起来比较麻烦,需要做转换,所以单独写了一个函数来处理
- Function Conv16BitRGB(ByVal num As Long) As Long
- Dim bit16 As Long
- Dim r As Byte, g As Byte, b As Byte, r1 As Byte, g1 As Byte, b1 As Byte
- '低5位表示Blue分量;中5为表示Green分量;高5位表示Red分量
- '16位数据的组成例如以下
- '第一个字节: g5g4g3b7b6b5b4b3
- '第二个字节:0r7r6r5r4r3g7g6
- '当中第二个字节的左边第一位为填充位?我在实验中用0填充?
- '该16位bmp图像无调色板数据
- '该16位bmp图像在显示时。图片浏览软件(如windows绘图)会将rgb555自己主动转换为rgb888显示。详细的方法例如以下
- 'b7b6b5b4b3->b7b6b5b4b3b7b6b5
- 'r7r6r5r4r3->r7r6r5r4r7r6r5
- 'g7g6g5g4g3->r7r6r5r4g7g6g5
- bit16 = num
- Call BitPlus.SHL(bit16, 17)
- Call BitPlus.SHR(bit16, 27)
- r = bit16
- r1 = bit16
- Call SHR(r1, 2)
- Call SHL(r, 3)
-
- r = r + r1
-
- bit16 = num
- Call BitPlus.SHL(bit16, 22)
- Call BitPlus.SHR(bit16, 27)
- g = bit16
- g1 = bit16
- Call SHR(g1, 2)
- Call SHL(g, 3)
- g = g + g1
-
- bit16 = num
- Call BitPlus.SHL(bit16, 27)
- Call BitPlus.SHR(bit16, 27)
- b = bit16
- b1 = bit16
- Call SHR(b1, 2)
- Call SHL(b, 3)
- b = b + b1
- 'Debug.Print Bin(r), Bin(g), Bin(b)
- Conv16BitRGB = VBA.rgb(r, g, b)
- End Function
复制代码
至此代码部分就算完成了,下面展示一下效果图
完整的附件在此:
vba提取bmp像素点图像信息并绘画.zip
(922.86 KB, 下载次数: 98)
|
评分
-
2
查看全部评分
-
|