首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > VB >

GDI+ IStream、StdPicture、Byte() 互转~散分~该如何解决

2013-01-06 
GDI+ IStream、StdPicture、Byte() 互转~~~散分~~~很多年前就想做一个远程控制的软件,只是一直以来图片的压

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的内容了,具体代码如下:

'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,具体代码如下:
'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,粘贴以下代码:
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

[解决办法]
好,支持分享。
[解决办法]

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

[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&, 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 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

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
[/Code]
[解决办法]
根本不需要引用那个tlb,直接定义As stdole.IUnknown 


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&, pvCreateStreamFromArray)
            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


[解决办法]


看到楼主在我博客的评论了,甚好甚好。
好帖就要推荐之,感谢楼主的分享。
[解决办法]
'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



[解决办法]
'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:

  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


Client Side:
  还原即可
[解决办法]
哇!果然是高质量的帖子啊!高手云集!学习innnnnnnngGDI+ IStream、StdPicture、Byte() 互转~散分~该如何解决
[解决办法]
211111111111111
[解决办法]
强力插入.................
[解决办法]
学习了

[解决办法]
ding  

[解决办法]
不懂图像处理的路过顶下...
[解决办法]
我记得以前有一份vb的远程桌面连接代码,可惜速度不太理想
如果是捕获全屏发送的话,速度和占用资源肯定是不行,还是
得直接发送修改了的地方这样才是比较好的一种方法。
[解决办法]
引用:
引用:
远程控制用JPG流在广域网上基本不太可能具有很大的实用性,除非双方的机器配置一流网络速度也一流。

是不管你用什么库也好,JPG压缩和解压终究是个耗时的工作。


没错啊。。。。全屏图像压缩始终是个治标不治本的方法。。。如果可以的话最好能找出屏幕改变的地方,然后对这些改变的地方进行压缩再处理。。。相当于在线将屏幕录像成视频数据流传输。。。。只是目前还比较菜,这个方法也只能从YY中实现了。。。适量YY有益健康哈。。。。


我等是菜鸟,看你们贴的代码大段大段的,感觉云里雾里的,

最好是:大虾们把想法用汉文说一下,我等菜鸟学习其主旨方法,忽略具体实现细节,这样对初学者帮助更大!

OK?
[解决办法]
学习了,不错。。。。

热点排行