Jump to content

GDI+ How to get binary image data from bitmap?


Mbee
 Share

Recommended Posts

You can readily create a bitmap from the binary data that's read in from a file (via a simple binary FileRead) using _GDIPlus_BitmapCreateFromMemory(). But how do you do the reverse?  That is, to generate/copy the binary data back from the bitmap so that it's the same as if you performed a binary FileRead()?

After reading all kinds of things on this issue, both here in the AutoIt fora and non-AutoIt sources elsewhere, I still haven't been able to find the answer. At first it seemed like _GDIPlus_BitmapLockBits() might be part of the solution, but I'm still a GDIPlus newbie and I can't understand if that's the right approach (and if so, how to actually code it all), or if there's an easier solution.

Any guidance, please?

Thanks!

Edited by Mbee
Link to comment
Share on other sites

Well, since no general assistance was provided for my OP, I've tried to figure it out on my own, with partial success.  Unfortunately, there's some kind of bug in the code I've tried to put together (I've borrowed heavily from Andreik's code seen here - thanks Andreik!).  Look for the comment reading "THIS IS WHERE I ALWAYS GET AN ERROR!". 

(Variables not defined in this function are global or otherwise supplied outside the func)

 

Func _MyBytesFromBitmap( ByRef $arg_Mem_Size, $arg_BitmapHdl, $arg_Tag_Encoder_Ptr )
;
;   NOTE: _GDIPlus_Startup() MUST have already been called
;
    Local $Lf_BitmapObjHdl = _GDIPlus_BitmapCreateFromHBITMAP($arg_BitmapHdl)
    If $Lf_BitmapObjHdl = 0 Then                                                ; Error in call
        $L_error = @error
        $L_extended = @extended
        SetError ( $L_error, $L_extended, "" )
        Return ""
    EndIf
;
    Local $Lf_Stream = DllCall($hGIFDLL__OLE32,"uint","CreateStreamOnHGlobal","ptr",0,"bool",1,"ptr*",0)
    If @error <> 0 Then
        $L_error = @error
        $L_extended = @extended
        SetError ( $L_error, $L_extended, "" )
        Return ""
    EndIf
;
    DllCall($G_GDIPlusSDllHdl,"uint","GdipSaveImageToStream","ptr",$Lf_BitmapObjHdl,"ptr",$Lf_Stream[3],"ptr",$arg_Tag_Encoder_Ptr,"ptr",0)
    If @error <> 0 Then
        $L_error = @error
        $L_extended = @extended
        SetError ( $L_error, $L_extended, "" )
        Return ""
    EndIf
;
    _GDIPlus_BitmapDispose($Lf_BitmapObjHdl)
;
    Local $Lf_Memory = DllCall("ole32.dll","uint","GetHGlobalFromStream","ptr",$Lf_Stream[3],"ptr*",0)
    If @error <> 0 Then
;-----> THIS IS WHERE I ALWAYS GET AN ERROR!
        $L_error = @error
        $L_extended = @extended
        SetError ( $L_error, $L_extended, "" )
        Return ""
    EndIf
;
    $arg_Mem_Size = _MemGlobalSize($Lf_Memory[2])
    Local $Lf_MemPtr = _MemGlobalLock($Lf_Memory[2])
    Local $Lf_MemStruct = DllStructCreate("byte[" & $arg_Mem_Size & "]", $Lf_MemPtr)
    Local $Lf_Data = DllStructGetData($Lf_MemStruct,1)
    _MemGlobalFree($Lf_Memory[2])
;
    SetError ( $L_error, $L_extended, $Lf_Data )
    Return $Lf_Data
EndFunc

What am I doing wrong there?

Thanks!

Edited by Mbee
Link to comment
Share on other sites

Try this:

;Coded by UEZ 2013 -> This program requires AutoIt version 3.3.9.21 or higher!
#include <GDIPlus.au3>
#include <Memory.au3>

_GDIPlus_Startup()
Global $sFile = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI\msoobe.jpg")
Global $hImage = _GDIPlus_ImageLoadFromFile($sFile)
Global $hBitmap = _GDIPlus_ImageResize($hImage, 10, 7)
Global $bImage = _GDIPlus_StreamImage2BinaryString($hBitmap)
ConsoleWrite("Error: " & @error & @LF)
ConsoleWrite(BinaryLen($bImage) & @CRLF)

MsgBox(0, "Binary", $bImage)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_Shutdown()

Func _GDIPlus_StreamImage2BinaryString($hBitmap, $sFormat = "JPG", $iQuality = 80, $bSave = False, $sFilename = @ScriptDir & "\Converted.jpg") ;coded by UEZ 2013 build 2014-01-25 (based on the code by Andreik)
    Local $sImgCLSID, $tGUID, $tParams, $tData
    Switch $sFormat
        Case "JPG"
            $sImgCLSID = _GDIPlus_EncodersGetCLSID($sFormat)
            $tGUID = _WinAPI_GUIDFromString($sImgCLSID)
            $tData = DllStructCreate("int Quality")
            DllStructSetData($tData, "Quality", $iQuality) ;quality 0-100
            Local $pData = DllStructGetPtr($tData)
            $tParams = _GDIPlus_ParamInit(1)
            _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData)
        Case "PNG", "BMP", "GIF", "TIF"
            $sImgCLSID = _GDIPlus_EncodersGetCLSID($sFormat)
            $tGUID = _WinAPI_GUIDFromString($sImgCLSID)
        Case Else
            Return SetError(1, 0, 0)
    EndSwitch
    Local $hStream = _WinAPI_CreateStreamOnHGlobal() ;http://msdn.microsoft.com/en-us/library/ms864401.aspx
    If @error Then Return SetError(2, 0, 0)
    _GDIPlus_ImageSaveToStream($hBitmap, $hStream, DllStructGetPtr($tGUID), DllStructGetPtr($tParams))
    If @error Then Return SetError(3, 0, 0)

    Local $hMemory = _WinAPI_GetHGlobalFromStream($hStream) ;http://msdn.microsoft.com/en-us/library/aa911736.aspx
    If @error Then Return SetError(4, 0, 0)
    Local $iMemSize = _MemGlobalSize($hMemory)
    If Not $iMemSize Then Return SetError(5, 0, 0)
    Local $pMem = _MemGlobalLock($hMemory)
    $tData = DllStructCreate("byte[" & $iMemSize & "]", $pMem)
    Local $bData = DllStructGetData($tData, 1)
    _WinAPI_ReleaseStream($hStream) ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms221473(v=vs.85).aspx
    _MemGlobalFree($hMemory)
    If $bSave Then
        Local $hFile = FileOpen($sFilename, 18)
        If @error Then Return SetError(6, 0, $bData)
        FileWrite($hFile, $bData)
        FileClose($hFile)
    EndIf
    Return $bData
EndFunc   ;==>_GDIPlus_StreamImage2BinaryString

It will return a binary string which can be saved.

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

That works!  Hey, thanks a great deal, UEZ!

A key part of my problem was that I was calling trancexx's _GIF_ResizeBitmap() instead of _GDIPlus_ImageResize().

I made a few minor changes to adapt your code to my naming standards and to adapt it to the fact that some external values were already known prior to the call. Here's the result...

;
;   Coded by UEZ 2013 -> This program requires AutoIt version 3.3.9.21 or higher!
;
Func _UEZ_BinaryStringFromBitmapHdl($hBitmap, $sFormat, $iQuality = 100)  ;coded by UEZ 2013 build 2014-01-25
                                                                          ; (based on the code by Andreik)
;
    Local $Lf_EncoderCLSIDStr = _GDIPlus_EncodersGetCLSID( $sFormat )     ; Get info for encoder of this pic type
    Local $Lf_EncoderGUIDTagStruct = _WinAPI_GUIDFromString($Lf_EncoderCLSIDStr)
    Local $Lf_EncoderGUIDTagStructPtr = DllStructGetPtr( $Lf_EncoderGUIDTagStruct )
;
    Local $tGUID, $tParams = 0, $tData
    $tGUID = $Lf_EncoderGUIDTagStruct
    If $sFormat = "JPG" Then
        $tData = DllStructCreate("int Quality")
        DllStructSetData($tData, "Quality", $iQuality) ;quality 0-100
        Local $pData = DllStructGetPtr($tData)
        $tParams = _GDIPlus_ParamInit(1)
        _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData)
    EndIf

    Local $hStream = _WinAPI_CreateStreamOnHGlobal() ;http://msdn.microsoft.com/en-us/library/ms864401.aspx
    If @error Then Return SetError(2, 0, 0)
    If $tParams <> 0 Then
        _GDIPlus_ImageSaveToStream($hBitmap, $hStream, $Lf_EncoderGUIDTagStructPtr, DllStructGetPtr($tParams))
    Else
        _GDIPlus_ImageSaveToStream($hBitmap, $hStream, $Lf_EncoderGUIDTagStructPtr)
    EndIf
    If @error Then Return SetError(3, 0, 0)

    Local $hMemory = _WinAPI_GetHGlobalFromStream($hStream) ;http://msdn.microsoft.com/en-us/library/aa911736.aspx
    If @error Then Return SetError(4, 0, 0)
    Local $iMemSize = _MemGlobalSize($hMemory)
    If Not $iMemSize Then Return SetError(5, 0, 0)
    Local $pMem = _MemGlobalLock($hMemory)
    $tData = DllStructCreate("byte[" & $iMemSize & "]", $pMem)
    Local $bData = DllStructGetData($tData, 1)
    _WinAPI_ReleaseStream($hStream) ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms221473(v=vs.85).aspx
    _MemGlobalFree($hMemory)
     Return $bData
EndFunc   ;==>_UEZ_BinaryStringFromBitmap

I am very grateful for your kind assistance!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...