我的程序需要显示一张很大的图片(10000*10000),这么的图片若一次性加载会导致计算机无法响应的。但其实我的程序在某一瞬间只显示其中的某一部分。有没有办法快速加载其中某一部分显示到PictureBox中?如X=100,Y=100,Height=500,Width=500的一小部分。图片是C:\a.bmp(有45M大)读取它应该需要很长时间的。我的意思是在读图片时就已经根据我需要的范围(100,100,500,500)进行读取?就读取这一区域的数据,这样加载到程序中的其实就是500*500的小图片虽然我不太了解,但BMP文件的格式是公开的吧。我要求处理24位真彩色的图片。
通过二进制文件访问的办法应该可以很快构造出哪一个“很小的区域”的图片吧。
请各位高手指点一下阿。
通过二进制文件访问的办法应该可以很快构造出哪一个“很小的区域”的图片吧。
请各位高手指点一下阿。
BMP文件是VisualBasic进行图像处理的主要文件格式。BMP文件由文件头(BITMAPFILEHEADER)、文件信息头(BITMAPINFOHEADER)、调色板(PALETTE)及图像数据(Data)组成。
文件头
Type BITMAPFILEHEADER'奇怪,.net不是不支持Type了么?
bfType As Integer'文件类型字段
bfSize As Long'文件的大小
bfReserved1 As Integer'保留(=0)
bfReserved2 As Integer'保留(=0)
bfOffBits As Long'第一个像素的偏移量
End Type文件信息头
Type BITMAPINFOHEADER
biSize As Long '文件信息头的长度(一般=40)
biWidth As Long'位图的宽度
biHeight As Long'位图的高度
biPlanes As Integer'平面的数目(=1)
biBitCount As Integer'每个象素所占位数(如=8,16,24)
biCompression As Long'是否压缩(为压缩=0)
biSizeImage As Long'图像大小(可选)(按字节)
biXPelsPerMeter As Long'未使用(=0)
biYPelsPerMeter As Long'未使用(=0)
biClrUsed As Long'位图使用的颜色数(使用所有颜色=0)
biClrImportant As Long'重要颜色数(都重要=0)
End Type调色板
Type PALETTENTRY
peRed As Byte'红色分量值
peGreen As Byte'绿色分量值
peBlue As Byte'蓝色分量值
peFlags As Byte'字母通道(指示透明度)
End Type数据图像(Data)
256色图像:数据部分的值并非是图像的颜色值,而是调色板的索引(index)值
24位真彩色图像:数据部分的值就是颜色值,因而24位真彩色位图没有调色板这一部分。注意,BMP图像的数据是按逆序存储的,即数据的第一行是屏幕显示的最后一行,因而采用顺序读取,逆序显示的方法;其次,BMP文件数据部分每一行像素数目都是4的倍数,这就意味着在读取数据时,应注意每一行的末端都可能包含有一些额外添加的(非像素值)字节,读取时应予忽略
Const MF_CHECKED = &H8&
Const MF_APPEND = &H100&
Const TPM_LEFTALIGN = &H0&
Const MF_DISABLED = &H2&
Const MF_GRAYED = &H1&
Const MF_SEPARATOR = &H800&
Const MF_STRING = &H0&
Const TPM_RETURNCMD = &H100&
Const TPM_RIGHTBUTTON = &H2&
Private Type POINTAPI
x As Long
y As Long
End Type
Private Declare Function CreatePopupMenu Lib "user32" () As Long
Private Declare Function TrackPopupMenuEx Lib "user32" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal x As Long, ByVal y As Long, ByVal HWnd As Long, ByVal lptpm As Any) As Long
Private Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal lpNewItem As Any) As Long
Private Declare Function DestroyMenu Lib "user32" (ByVal hMenu As Long) As Long
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Dim hMenu As Long
Private Sub Form_MouseUp(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim Pt As POINTAPI
Dim ret As Long
hMenu = CreatePopupMenu()
AppendMenu hMenu, MF_STRING, 1, "Hello !"
AppendMenu hMenu, MF_GRAYED Or MF_DISABLED, 2, "Testing ..."
AppendMenu hMenu, MF_SEPARATOR, 3, ByVal 0&
AppendMenu hMenu, MF_CHECKED, 4, "TrackPopupMenu"
GetCursorPos Pt
ret = TrackPopupMenuEx(hMenu, TPM_LEFTALIGN Or TPM_RETURNCMD Or TPM_RIGHTBUTTON, Pt.x, Pt.y, Me.HWnd, ByVal 0&)
DestroyMenu hMenu
Debug.Print ret
End Sub
不过如果全部都是24位色的图片的话,有一个投机取巧的方法,
因为24位色BMP没有调色板,使用2进制方法打开文件,直接跳过文件头的长度,动数据段中读你需要的部分。此方法一旦用于其他颜色位的BMP立刻出错。
既然是缓冲,你就得当场全部读到控件里或变量里,要能直接读,楼主也不用费这么大力气了,等于没说。呵呵.只是记得在学.Net时有个例子说什么双缓冲来的,所以就说一下提个醒呗.其实我也不会.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'On Error GoTo err1 '错误捕获
Dim i As Integer
Dim imgByte() As Byte
Dim Pic As Image
'Pic = New Bitmap("C:\222.bmp")
'PictureBox1.Image = Pic
'PictureBox1.Image.Width = 1000
'Dim ImageBMP As System.Drawing.Bitmap = PictureBox1.Image
Dim ImageBmp As New System.Drawing.Bitmap(800, 600) 'FileOpen(1, "c:\1\1.gif", OpenMode.Binary, OpenAccess.Read)
Dim fname As String = "C:\000.bmp"
Dim newBmp As Byte
Dim firstPos As Byte
FileOpen(1, fname, OpenMode.Binary, OpenAccess.Read)
'FileOpen(2, "C:\111.bmp", OpenMode.Binary, OpenAccess.ReadWrite)
FileSystem.Seek(1, 10 + 1)
FileGet(1, firstPos) '得到像素颜色的开始的位置 firstspos 'For i = 1 To firstPos
' FileSystem.Seek(2, i)
' FileSystem.Seek(1, i)
' FileGet(1, newBmp)
' If i = 19 Then newBmp = 10
' If i = 23 Then newBmp = 10
' FilePut(2, newBmp)
'Next
'For i = firstPos + 1 To LOF(1) / Len(newBmp)
' FileSystem.Seek(2, i)
' FileSystem.Seek(1, i)
' FileGet(1, newBmp)
' FilePut(2, newBmp)
'Next
MsgBox("!")
'firstPos = firstPos + 1 '取消误差 Dim bmpGeshi1 As Byte '判断是不是BMP图片
Dim bmpGeshi2 As Byte
FileSystem.Seek(1, 1)
FileGet(1, bmpGeshi1)
FileSystem.Seek(1, 2)
FileGet(1, bmpGeshi2) ''UPGRADE_WARNING: Get 已升级到 FileGetObject 并具有新行为。 单击以获得更多信息:“ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1041"”
'FileGetObject(1, 1, bmpGeshi1)
''UPGRADE_WARNING: Get 已升级到 FileGetObject 并具有新行为。 单击以获得更多信息:“ms-help://MS.VSCC.2003/commoner/redir/redirect.htm?keyword="vbup1041"”
'FileGetObject(1, 2, bmpGeshi2) If bmpGeshi1 = 66 And bmpGeshi2 = 77 Then
'Picture2.Print("BMP")
Else
'Picture2.Print("格式错误")
MsgBox("ERROR")
FileClose(1)
Exit Sub
End If If geShi() = 24 Then '判断BMP图片位数
'Picture2.Print("24位")
Else
MsgBox("文件是" + geShi() + "位格式暂时不支持,请打开24位格式的BMP")
FileClose(1)
Exit Sub
End If Dim maxX As Long '存图片宽, 附:定义为integer时pos值溢出 找到图片大小
Dim maxY As Long '存图片高
Dim x1 As Byte, x2 As Byte, x3 As Byte, x4 As Byte
Dim y1 As Byte, y2 As Byte, y3 As Byte, y4 As Byte
FileSystem.Seek(1, 18 + 1)
FileGet(1, x1) '最低位 需要加 1
FileSystem.Seek(1, 19 + 1)
FileGet(1, x2)
FileSystem.Seek(1, 20 + 1)
FileGet(1, x3)
FileSystem.Seek(1, 21 + 1)
FileGet(1, x4) '最高位
maxX = x1 + x2 * 256 + x3 * 256 * 256 + x4 * 256 * 256 * 256 '得到横坐标x
Dim X10 As Byte, X20 As Byte, X30 As Byte, X40 As Byte
FileSystem.Seek(1, 22 + 1)
FileGet(1, y1) '最低位
FileSystem.Seek(1, 23 + 1)
FileGet(1, y2)
FileSystem.Seek(1, 24 + 1)
FileGet(1, y3)
FileSystem.Seek(1, 25 + 1)
FileGet(1, y4) '最高位
'Picture2.Print "y1="; y1; "y2="; y2; "y3="; y3; "y4="; y4
maxY = y1 + y2 * 256 + y3 * 256 * 256 + y4 * 256 * 256 * 256 '得到纵坐标y
'Picture2.Print "图像大小:宽"; maxX; "高"; maxY 'Picture1.Picture = LoadPicture() '清空图片框
'Picture2.Picture = LoadPicture()
Dim pos As Long '文件中点的坐标 开始描点
Dim ix As Integer 'BMP文件中的位置
Dim iy As Integer 'BMP文件中的位置
Dim ix2 As Integer '用于描点的坐标
Dim iy2 As Integer '用于描点的坐标
Dim yanseRed As Byte '存红色的值
Dim yanseGreen As Byte '存绿色的值
Dim yanseBlue As Byte '存蓝色的值
'Dim firstPos As Byte
ix2 = 1 '用于描x坐标
iy2 = maxY - 1 '用于描y坐标 附:由于 测试时顶头空了一行(多了行空白),所以-1
'FileSystem.Seek(1, 10 + 1)
'FileGet(1, firstPos) '得到像素颜色的开始的位置 firstspos
firstPos = firstPos + 1 '取消误差
'Picture2.Print "像素点开始位置"; firstPos
'For iy = 1 To 10 'y行
' For ix = 1 To 10
' Dim PosB
' pos = firstPos + (iy - 1) * maxX * 3 + (ix - 1) * 3
' PosB = firstPos + (iy - 1) * 10 * 3 + (ix - 1) * 3
' FileSystem.Seek(1, pos)
' FileGet(1, yanseBlue)
' FileSystem.Seek(2, PosB)
' FilePut(2, 0) ' FileSystem.Seek(1, pos + 1)
' FileGet(1, yanseBlue)
' FileSystem.Seek(2, PosB + 1)
' FilePut(2, 0) ' FileSystem.Seek(1, pos + 2)
' FileGet(1, yanseBlue)
' FileSystem.Seek(2, PosB + 2)
' FilePut(2, 255)
' Next
'Next
ProgressBar1.Maximum = maxY
FileSystem.Seek(1, firstPos)
Dim c As System.Drawing.Color
For iy = 1 To maxY 'y行
ix2 = 0 '下一行起点
For ix = 1 To maxX 'x列
'pos = firstPos + (iy - 1) * maxX * 3 + (ix - 1) * 3 '计算文件指针位置 iy-1,ix-1取消误差
'Picture2.Print "pos=", pos
'FileSystem.Seek(1, pos)
FileGet(1, yanseBlue) '得到颜色
'FileSystem.Seek(1, pos + 1)
FileGet(1, yanseGreen) '得到颜色
'FileSystem.Seek(1, pos + 2)
FileGet(1, yanseRed) '得到颜色
'Picture1.PSet (ix2, iy2), RGB(yanseRed, yanseGreen, yanseBlue) '描点
'PictureBox1.Image()
c = ImageBmp.GetPixel(ix2, iy2)
c = c.FromArgb(yanseRed, yanseGreen, yanseBlue) ImageBmp.SetPixel(ix2, iy2, c)
ix2 = ix2 + 1 '下一个点的位置
Next ix
ix2 = 0 '下一行起点
iy2 = iy2 - 1 '下一行
'Picture2.Print "iy2=", iy2
'System.Windows.Forms.Application.DoEvents()
'ProgressBar1.Value = iy
'PictureBox1.Image = ImageBmp
Next iy
PictureBox1.Image = ImageBmp
'FileClose(2)
FileClose(1) '正常时关闭文件
Exit Sub
'FileClose(1)
err1: '错误处理
'If Err() = 32755 Then Exit Sub '取消打开文件,发生32755错误错误
'Picture2.Print("发生错误")
FileClose(1) '有错误时关闭文件
Exit Sub End Sub
Private Function geShi() As Integer '判断是几位BMP格式
Dim gs As Byte
FileSystem.Seek(1, 28 + 1)
FileGet(1, gs) '得到颜色
geShi = gs
End Function
把从BMP文件中提取出来的数据重新形成一个小文件,再用Picture直接读取
不想形成文件的也可以采用再内存中直接形成位图文件格式后转换
相信比你直接一点一点地画来的快
先把位图分割成100来张图片,文件名用一定的顺序
一张几百K,要用的时候将其读入,用bitblt函数应该可以解决问题了
1、将大图片读入内存,用API函数(不难做到);
2、LoadImage
CreateCompatibleDC
SelectObject...
3、用BitBlt拷取任一区块。