GDI+ IStream、StdPicture、Byte() 互转~~~散分~~~
很多年前就想做一个远程控制的软件,只是一直以来图片的压缩速度总是提升不上去,而我也参考过很多网上的关于图片压缩的例子,比如zyl910的GIF_LZW压缩方法,Huffman压缩方法,以至到GDI+的直接生成JPG、PNG的方法(这种方法无论从压缩率和速度上都是最佳的,可惜这种方法网上一直没找到直接保存为Byte()的例子,见得最多的例子就是用GdipSaveImageToFile保存到磁盘,然后再读取发送了,但是我做的可是远程控制软件,每秒不知道要写多少M的数据进磁盘!),近来在偶然机会重新拾起了完成这个程序的念头,而且很巧的是搜索到了Modest的《VB6结合GDI+实现内存(Stream)压缩/解压缩JPG(JPEG)图像》,这篇文章给了我很大的启发,在此感谢Modest!!!
Modest的代码已经实现了StdPicture和IStream的互转,我另外使用了GlobalAlloc、GlobalLock、GlobalUnlock、GlobalFree等函数创建一个缓冲区(指针为hGlobal),将Modest代码中CreateStreamOnHGlobal(ByVal 0&, False, picStream)改成CreateStreamOnHGlobal(ByVal hGlobal, False, picStream),这样我便可根据hGlobal来读写picStream的内容了,具体代码如下:
- VB code
'By TZWSOHO '从图像转换为流再转为字节数组 Public Function PictureToByteArray(ByVal Picture As StdPicture, Optional ByVal JpegQuality As Long = 85) As Byte() Dim picStream As IStream Dim lBitmap As Long Dim tGUID As GUID Dim bytBuff() As Byte Dim tParams As EncoderParameters Dim lngGdipToken As Long Dim hGlobal As Long, lpBuffer As Long, dwSize As Long, Buff() As Byte lngGdipToken = StartUpGDIPlus(GdiPlusVersion) '检查JPG压缩比率 If JpegQuality > 100 Then JpegQuality = 100 If JpegQuality < 0 Then JpegQuality = 0 '创建Bitmap If GdipCreateBitmapFromHBITMAP(Picture.Handle, 0, lBitmap) = OK Then hGlobal = GlobalAlloc(GMEM_MOVEABLE, Picture.Width * Picture.Height \ 256) '创建缓冲区 '创建Stream If CreateStreamOnHGlobal(ByVal hGlobal, False, picStream) = 0 Then '转换GUID If CLSIDFromString(StrPtr(ClsidJPEG), tGUID) = 0 Then '设置JPG相关参数值 tParams.Count = 1 With tParams.Parameter(0) CLSIDFromString StrPtr(EncoderQuality), .GUID .NumberOfValues = 1 .Type = EncoderParameterValueTypeLong .Value = VarPtr(JpegQuality) End With '将Bitmap数据保存到流(JPG格式) If GdipSaveImageToStream(lBitmap, picStream, tGUID, tParams) = OK Then 'GetHGlobalFromStream picStream, hGlobal picStream.Seek 0, STREAM_SEEK_CUR, dwSize '获取图像大小 lpBuffer = GlobalLock(hGlobal) '获取缓冲区读写指针 ReDim Buff(dwSize - 1): CopyMemory Buff(0), ByVal lpBuffer, dwSize '读取图像 GlobalUnlock hGlobal: GlobalFree hGlobal '释放分配的缓冲区空间 PictureToByteArray = Buff End If End If Set picStream = Nothing End If End If GdipDisposeImage lBitmap GdiplusShutdown lngGdipToken End Function
若要把Byte()转化为StdPicture,我的方法是先用CreateStreamOnHGlobal把Byte()转化为IStream,然后再调用Modest代码里面的StreamToPicture函数最终转化为StdPicture,具体代码如下:
- VB code
'By TZWSOHO '从字节数组转换为流再转换为图像 Public Function ByteArrayToPicture(sBuf() As Byte) As StdPicture Dim picStream As IStream Dim lBitmap As Long Dim hBitmap As Long Dim lngGdipToken As Long Dim tPictDesc As PICTDESC Dim IID_IPicture As IID Dim oPicture As IPicture Dim hGlobal As Long, lpBuffer As Long lngGdipToken = StartUpGDIPlus(GdiPlusVersion) hGlobal = GlobalAlloc(GMEM_MOVEABLE, UBound(sBuf) + 1) '创建缓冲区 lpBuffer = GlobalLock(hGlobal) '获取缓冲区读写指针 CopyMemory ByVal lpBuffer, sBuf(0), UBound(sBuf) + 1 '复制字节数组内容到缓冲区 '创建Stream If CreateStreamOnHGlobal(ByVal hGlobal, False, picStream) = 0 Then '从Stream加载Bitmap If GdipLoadImageFromStream(picStream, lBitmap) = OK Then '根据Bitmap创建hBitbmp If GdipCreateHBITMAPFromBitmap(lBitmap, hBitmap, 0) = OK Then With tPictDesc .cbSizeOfStruct = Len(tPictDesc) .picType = vbPicTypeBitmap .hgdiObj = hBitmap .hPalOrXYExt = 0 End With ' 初始化IPicture With IID_IPicture .Data1 = &H7BF80981 .Data2 = &HBF32 .Data3 = &H101A .Data4(0) = &H8B .Data4(1) = &HBB .Data4(3) = &HAA .Data4(5) = &H30 .Data4(6) = &HC .Data4(7) = &HAB End With Call OleCreatePictureIndirect(tPictDesc, IID_IPicture, True, oPicture) Set ByteArrayToPicture = StreamToPicture(picStream) End If End If GlobalUnlock hGlobal: GlobalFree hGlobal '释放分配的缓冲区空间 Set picStream = Nothing End If GdipDisposeImage lBitmap GdiplusShutdown lngGdipToken End Function
完整的模块代码太长了。。。请到我空间看。。。
如果要测试,可以把以上代码保存成一个模块,然后创建一个新的窗体,放置一个Picture1(加载一张图片)、一个Picture2(留空白)、一个Command1,粘贴以下代码:
- VB code
Option Explicit'*********************************************************************************'StdPicture、IStream、Byte() 互转'作者:TZWSOHO''参考了魏滔序的《VB6 结合 GDI+ 实现内存(Stream)压缩/解压缩 JPG 图像》'http://blog.csdn.net/Modest/archive/2009/08/31/4505237.aspx'非常感谢魏滔序的代码!!!''欢迎访问我的博客:http://blog.csdn.net/tzwsoho'*********************************************************************************'示例Private Sub Command1_Click() 'By Modest 'Dim s As IStream 'Set s = PictureToStream(Picture1.Picture, 5) 'Set Picture2.Picture = StreamToPicture(s) 'By TZWSOHO Dim Buf() As Byte Buf = PictureToByteArray(Picture1.Picture, 5) Set Picture2.Picture = ByteArrayToPicture(Buf)End Sub
[解决办法]
好,支持分享。
[解决办法]
- VB code
Private Function CreateStreamFromArray(ArrayPtr As Long, Length As Long) As stdole.IUnknown ' Purpose: Create an IStream-compatible IUnknown interface containing the ' passed byte aray. This IUnknown interface can be passed to GDI+ functions ' that expect an IStream interface -- neat hack On Error GoTo HandleError Dim o_hMem As Long Dim o_lpMem As Long If ArrayPtr = 0& Then CreateStreamOnHGlobal 0&, 1&, pvCreateStreamFromArray ElseIf Length <> 0& Then o_hMem = GlobalAlloc(&H2&, Length) If o_hMem <> 0 Then o_lpMem = GlobalLock(o_hMem) If o_lpMem <> 0 Then CopyMemory ByVal o_lpMem, ByVal ArrayPtr, Length Call GlobalUnlock(o_hMem) Call CreateStreamOnHGlobal(o_hMem, 1&, pvCreateStreamFromArray) End If End If End If HandleError:End Function
[解决办法]
顶
[解决办法]
根本不需要引用那个tlb,直接定义As stdole.IUnknown
[解决办法]
根本不需要引用那个tlb,直接定义As stdole.IUnknown
- VB code
Private Function StreamToStdPicture(hStream As Long) As IPicture ' function creates a stdPicture from the passed array ' Note: The array was already validated as not empty before this was called Dim aGUID(0 To 3) As Long aGUID(0) = &H7BF80980 ' GUID for stdPicture aGUID(1) = &H101ABF32 aGUID(2) = &HAA00BB8B aGUID(3) = &HAB0C3000 Call OleLoadPicture(ByVal hStream, 0&, 0&, aGUID(0), StreamToStdPicture)End FunctionPrivate Function CreateStreamFromArray(ArrayPtr As Long, Length As Long) As stdole.IUnknown ' Purpose: Create an IStream-compatible IUnknown interface containing the ' passed byte aray. This IUnknown interface can be passed to GDI+ functions ' that expect an IStream interface -- neat hack On Error GoTo HandleError Dim o_hMem As Long Dim o_lpMem As Long If ArrayPtr = 0& Then CreateStreamOnHGlobal 0&, 1&, CreateStreamFromArray ElseIf Length <> 0& Then o_hMem = GlobalAlloc(&H2&, Length) If o_hMem <> 0 Then o_lpMem = GlobalLock(o_hMem) If o_lpMem <> 0 Then CopyMemory ByVal o_lpMem, ByVal ArrayPtr, Length Call GlobalUnlock(o_hMem) Call CreateStreamOnHGlobal(o_hMem, 1&, pvCreateStreamFromArray) End If End If End If HandleError:End FunctionPrivate Function ArrayFromStream(hStream As Long, arrayBytes() As Byte) As Boolean ' Return the array contained in an IUnknown interface (stream) Dim o_hMem As Long, o_lpMem As Long Dim o_lngByteCount As Long If hStream Then If GetHGlobalFromStream(ByVal hStream, o_hMem) = 0 Then o_lngByteCount = GlobalSize(o_hMem) If o_lngByteCount > 0 Then o_lpMem = GlobalLock(o_hMem) If o_lpMem <> 0 Then ReDim arrayBytes(0 To o_lngByteCount - 1) CopyMemory arrayBytes(0), ByVal o_lpMem, o_lngByteCount GlobalUnlock o_hMem ArrayFromStream = True End If End If End If End If End Function
[解决办法]
[解决办法]
'Revised
- VB code
Private Function StreamToStdPicture(hStream As Long) As IPicture ' function creates a stdPicture from the passed array ' Note: The array was already validated as not empty before this was called Dim aGUID(0 To 3) As Long aGUID(0) = &H7BF80980 ' GUID for stdPicture aGUID(1) = &H101ABF32 aGUID(2) = &HAA00BB8B aGUID(3) = &HAB0C3000 Call OleLoadPicture(ByVal hStream, 0&, 0&, aGUID(0), StreamToStdPicture)End FunctionPrivate Function CreateStreamFromArray(ArrayPtr As Long, Length As Long) As stdole.IUnknown ' Purpose: Create an IStream-compatible IUnknown interface containing the ' passed byte aray. This IUnknown interface can be passed to GDI+ functions ' that expect an IStream interface -- neat hack On Error GoTo HandleError Dim o_hMem As Long Dim o_lpMem As Long If ArrayPtr = 0& Then CreateStreamOnHGlobal 0&, 1&, CreateStreamFromArray ElseIf Length <> 0& Then o_hMem = GlobalAlloc(&H2&, Length) If o_hMem <> 0 Then o_lpMem = GlobalLock(o_hMem) If o_lpMem <> 0 Then CopyMemory ByVal o_lpMem, ByVal ArrayPtr, Length Call GlobalUnlock(o_hMem) Call CreateStreamOnHGlobal(o_hMem, 1&, CreateStreamFromArray) End If End If End If HandleError:End FunctionPrivate Function ArrayFromStream(hStream As Long, arrayBytes() As Byte) As Boolean ' Return the array contained in an IUnknown interface (stream) Dim o_hMem As Long, o_lpMem As Long Dim o_lngByteCount As Long If hStream Then If GetHGlobalFromStream(ByVal hStream, o_hMem) = 0 Then o_lngByteCount = GlobalSize(o_hMem) If o_lngByteCount > 0 Then o_lpMem = GlobalLock(o_hMem) If o_lpMem <> 0 Then ReDim arrayBytes(0 To o_lngByteCount - 1) CopyMemory arrayBytes(0), ByVal o_lpMem, o_lngByteCount GlobalUnlock o_hMem ArrayFromStream = True End If End If End If End If End Function
[解决办法]
'Revised
Private Function StreamToStdPicture(hStream As Long) As IPicture
' function creates a stdPicture from the passed array
' Note: The array was already validated as not empty before this was called
Dim aGUID(0 To 3) As Long
aGUID(0) = &H7BF80980 ' GUID for stdPicture
aGUID(1) = &H101ABF32
aGUID(2) = &HAA00BB8B
aGUID(3) = &HAB0C3000
Call OleLoadPicture(ByVal hStream, 0&, 0&, aGUID(0), StreamToStdPicture)
End Function
Private Function CreateStreamFromArray(ArrayPtr As Long, Length As Long) As stdole.IUnknown
' Purpose: Create an IStream-compatible IUnknown interface containing the
' passed byte aray. This IUnknown interface can be passed to GDI+ functions
' that expect an IStream interface -- neat hack
On Error GoTo HandleError
Dim o_hMem As Long
Dim o_lpMem As Long
If ArrayPtr = 0& Then
CreateStreamOnHGlobal 0&, 1&, CreateStreamFromArray
ElseIf Length <> 0& Then
o_hMem = GlobalAlloc(&H2&, Length)
If o_hMem <> 0 Then
o_lpMem = GlobalLock(o_hMem)
If o_lpMem <> 0 Then
CopyMemory ByVal o_lpMem, ByVal ArrayPtr, Length
Call GlobalUnlock(o_hMem)
Call CreateStreamOnHGlobal(o_hMem, 1&, CreateStreamFromArray)
End If
End If
End If
HandleError:
End Function
Private Function ArrayFromStream(hStream As Long, arrayBytes() As Byte) As Boolean
' Return the array contained in an IUnknown interface (stream)
Dim o_hMem As Long, o_lpMem As Long
Dim o_lngByteCount As Long
If hStream Then
If GetHGlobalFromStream(ByVal hStream, o_hMem) = 0 Then
o_lngByteCount = GlobalSize(o_hMem)
If o_lngByteCount > 0 Then
o_lpMem = GlobalLock(o_hMem)
If o_lpMem <> 0 Then
ReDim arrayBytes(0 To o_lngByteCount - 1)
CopyMemory arrayBytes(0), ByVal o_lpMem, o_lngByteCount
GlobalUnlock o_hMem
ArrayFromStream = True
End If
End If
End If
End If
End Function
[解决办法]
gz
[解决办法]
正在找这个,谢谢啦!
[解决办法]
远程控制用JPG流在广域网上基本不太可能具有很大的实用性,除非双方的机器配置一流网络速度也一流。
是不管你用什么库也好,JPG压缩和解压终究是个耗时的工作。
[解决办法]
默默地走过,支持!
[解决办法]
SF
[解决办法]
直接传个工程 看看不就行了
[解决办法]
顶了慢慢看…………
[解决办法]
顶了慢慢看…………
[解决办法]
[解决办法]
好样的程序!
顶一把!
[解决办法]
mark
[解决办法]
这类的程序其实要这样做:(Just idea)
Server side:
- VB code
DTHwnd = GetDesktopWindow() DTHdc = GetDC(DeskHwnd) Ret = GetWindowRect(DTHwnd , DTRect) '### create 16 clolored DIB or 24 or 32bpp DIB取决于要求质量 DIB.Colors = 16 '将Screen分成若干份例如 5x5 Call DIB.Create(DTRect.Right / 5, DTRect.Bottom / 5)Do Until ENDE For yPos = 0 To DTRect.Bottom Step (DTRect.Bottom / 5) For xPos = 0 To DTRect.Right Step (DTRect.Right / 5) '### blit actual part of the desktop into DIB Ret = BitBlt(DIB.hdc, 0, 0, DTRect.Right / 5, DTRect.Bottom / 5, DTHdc, xPos, yPos, SRCCOPY) Call DIB.ToByte(ByteArray) '### 用Zlib或者其它compress the array Call ZLib.CompressByte(ByteArray) '### save the checksum 用CRC结果进行比较,如果相同就不送,这样就节省很多带宽!!! CS_Tmp = calcCRC32(ByteArray) '### if the part is different to the last-> send the data If CS_Tmp <> CS(K) Then CS(K) = CS_Tmp On Error GoTo NoConn '### first send the actual position frmCapture.TCP_Set.SendData CStr(xPos) & ";" & CStr(yPos) '### wait for response Do Until C_Set_Response DoEvents Loop C_Set_Response = False '### send data frmCapture.TCP.SendData ByteArray '### wait for response Do Until C_Response DoEvents Loop C_Response = False On Error GoTo 0 End If '### next part of the desktop... K = K + 1 DoEvents Next xPos Next yPos '### begin at pos (0,0) xPos = 0 yPos = 0 K = 0 '### one frame made Q = Q + 1 Loop
[解决办法]
哇!果然是高质量的帖子啊!高手云集!学习innnnnnnng
[解决办法]
211111111111111
[解决办法]
强力插入.................
[解决办法]
学习了
[解决办法]
ding
[解决办法]
不懂图像处理的路过顶下...
[解决办法]
我记得以前有一份vb的远程桌面连接代码,可惜速度不太理想
如果是捕获全屏发送的话,速度和占用资源肯定是不行,还是
得直接发送修改了的地方这样才是比较好的一种方法。
[解决办法]
[解决办法]
学习了,不错。。。。