c.haslam

DllStructCreate not allocating memory for byte array?

12 posts in this topic

#1 ·  Posted (edited)

In the code that follows, it appears that DllStructCreate() is not allocating memory in

$tag = 'byte val['&$iLength&']'
.
.
Static Local $tvalue = DllStructCreate($tag)

Running the code below, the problem does not show, but it does show in a much longer script: there _GDIPlus_ImageSaveToFile only writes about 30K bytes when it should write about 1MB, which it does when the code is

$tag = 'char val['&$iLength&']'
.
.
Static Local $tvalue = DllStructCreate($tag)

It should write about 1 MB.

I suggest caution in running the code below. On my PC, it caused a second instance of SciTE to appear at the top left of the Desktop, showing only the title bar, with a width of only approximately 200 pixels! Then rebooting the PC showed this at login. Further, double-clicking on the SciTE shortcut on the Desktop showed SciTE in the same way! (After running Regedit, and searching for SciTE, the shortcut behaves normally.)

I would appreciate help in determining whether or not there is a bug in DLLStructCreate, preferably a way which does not clobber Windows. Of course, It is possible that there is a bug in my code.

I have made $tvalue Static in the hope that this might make the code run properly. Doing this did not help.

My code is based on code written by Authenticity and ChrisL.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12
main()

Func main()
    _GDIPlus_Startup()

    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')

    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $propsAr[UBound($ar,1)-1][5],$vec,$j=-1
    For $i = 1 To $ar[0][0]
        If $ar[$i][3]<>0 Then   ; pValue -- for Sony!
            $j += 1
            $propsAr[$j][0] = $ar[$i][0]    ; id
            $propsAr[$j][1] = $ar[$i][1]    ; length
            $propsAr[$j][2] =$ar[$i][2]; type
            $vec = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
            $propsAr[$j][3] = $vec[0]   ; val1
            Switch $ar[$i][2]
                Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                    $propsAr[$j][4] = $vec[1]
                Case Else
                    $propsAr[$j][4] = ''
            EndSwitch
        EndIf
    Next
    ReDim $propsAr[$j+1][5]

    For $i = 0 To UBound($propsAr,1)-1
        Switch $propsAr[$i][2]  ; type
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3],$propsAr[$i][4])
            Case Else
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3])
        EndSwitch
    Next
    _GDIPlus_ImageSaveToFile($hImage,'H:\b\1.jpg')
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Switch $iType
        Case 1,6    ; $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val',$pValue)
        Case 2      ; $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&']',$pValue)
        Case 3      ; $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val',$pValue)
        Case 4      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 5      ; $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 7      ; $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tvalue = DllStructCreate('byte val['&$ilength&']',$pValue) ; see _GDIPlus_ImageSetPropertyItemEx
;~          $tvalue = DllStructCreate('char val['&$ilength&']',$pValue)
        Case 8      ; $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val',$pValue)
        Case 9      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 10     ; $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 11     ; $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val',$pValue)
        Case 12     ; $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
    If @error Then Return SetError(@error,0,-1)
    Switch $iType
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            Local $aRet[2]
            $aRet[0] = DllStructGetData($tvalue,'val1')
            $aRet[1] = DllStructGetData($tvalue,'val2')
        Case Else
            Local $aRet[1]
            $aRet[0] = DllStructGetData($tvalue,'val')
    EndSwitch
    Return $aRet
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value1,$value2=-1)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tag = 'byte val'
        Case $GDIP_PROPERTYTAGTYPEASCII
            $tag = 'char val['&$iLength&']'
        Case $GDIP_PROPERTYTAGTYPEUSHORT
            $tag = 'ushort val'
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tag = 'ulong val'
        Case $GDIP_PROPERTYTAGTYPEURATIONAL
            $tag =  'ulong val1;ulong val2'
        Case $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tag = 'byte val['&$iLength&']' ; causes saving to jpeg to write junk
;~          $tag = 'char val['&$iLength&']'
        Case $GDIP_PROPERTYTAGTYPESSHORT
            $tag = 'short val'
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tag = 'ulong val'
        Case $GDIP_PROPERTYTAGTYPESRATIONAL
            $tag =  'long val1;long val2'
        Case $GDIP_PROPERTYTAGTYPESFLOAT
            $tag = 'float val'
        Case $GDIP_PROPERTYTAGTYPEDFLOAT
            $tag = 'double val'
    EndSwitch
    Static Local $tvalue = DllStructCreate($tag)
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            DllStructSetData($tvalue,'val1',$value1)
            DllStructSetData($tvalue,'val2',$value2)
        Case Else
            DllStructSetData($tvalue,1,$value1)
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))

    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    DllStructGetPtr($tProp))
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Return $aResult[0] = 0
EndFunc

I have seen $iLength be as much as 37 KB for $GDIP_PROPERTYTAGTYPEUNDEFINED.

Is there is a bug, how can this be demonstrated to the developers in a few lines of code (without clobbering Windows)?

 

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

I have now found, with code similar to that in the first post, that a script can produce different results when run several times. My new testing shows that there are inconsistent results in handling property types other than $GDIP_PROPERTYTAGTYPEUNDEFINED.

A result consists of:

  • ArrayDisplay .png file, showing the id, length, type and value(s) of each of the properties
  • Output file size reported by the script
  • @extended (GDIPLUS Status value) from _GDIPlus_ImageSaveToFile()

I know what val1 and val2 should be from Irfanview. The output file size should be 916K, it being a copy of the input file. The Status value should be zero.

I ran the script 7 times:

  • 3 displayed the values of the first 27 properties OK in _ArrayDisplay() and most of properties 30 to 63 as zeroes when they should be non-zero, output file size as 36K and GDIP Status as 7 (Win32 error)
  • 3 displayed the values of all properties correctly in _ArrayDisplay(), file size as 916K and GDIP status as 0 (OK)
  • 1 the values of the first 27 properties as zeros (which is wrong), file size as 916K and GDIP status as 0 (OK)

The PC is running AOK (except as noted in Post1, which happened once). I have an AutoIt script which runs automatically daily with no problems. So the cause of this odd behavior is either a subtle error in my code or a bug in AutoIt.

My thoughts turn to the idea that there is a memory leak somewhere, possibly caused by my code. The input file has not changed.

I am wondering how I can allocate memory to structs independent of DllStructCreate(). Doing so might identify the problem. Do I use _MemGlobalAlloc() to allocate memory and _MemGlobalFree() to release it in _GDIPlus_ImageGetPropertyItemValue()? If so, with what parameters?

The code follows, and the attached .png's show _ArrayDisplay() results.

 

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
;~ #include "..\cDebug.au3"

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()

    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
    Local $propsAr = GetProperties($hImage)

Local $dAr = $propsAr
For $i = 0 To UBound($propsAr,1)-1
    $dAr[$i][0] = Hex($propsAr[$i][0],4)
Next
_ArrayDisplay($dAr,'At line '&@ScriptLineNumber,'',0,Default,'id|length|type|val1|val2')

    For $i = 0 To UBound($propsAr,1)-1
        Switch $propsAr[$i][2]  ; type
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3],$propsAr[$i][4])
            Case Else
                _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
                    $propsAr[$i][2],$propsAr[$i][3])
        EndSwitch
    Next
    $propsAr = GetProperties($hImage)   ; causes save file to fail
;~ _ArrayDisplay($propsAr,@ScriptLineNumber)
    Local $ouFilspc = 'H:\b\1.jpg'
    _GDIPlus_ImageSaveToFile($hImage,$ouFilspc)
    If @error Then MsgBox(0,'_GDIPlus_ImageSaveToFile','@error '&@error&' @extended '&@extended)
    _GDIPlus_ImageDispose($hImage)
    Local $vec = FileGetTime($ouFilspc)
    MsgBox(0,@ScriptLineNumber,'Output: file size '&FileGetSize($ouFilspc)& _
        ' bytes at '&StringFormat('%s-%02s-%02s %02s:%02s',$vec[0],$vec[1],$vec[2],$vec[3],$vec[4]))
EndFunc

Func GetProperties($hImage)
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $propsAr[UBound($ar,1)-1][5],$vec,$j=-1
    For $i = 1 To $ar[0][0]
        If Not ($ar[$i][3]=0 Or ($ar[$i][2]=2 And $ar[$i][1]=0)) Then   ; pValue, type, length -- for Sony!
            $j += 1
            $propsAr[$j][0] = $ar[$i][0]    ; id
            $propsAr[$j][1] = $ar[$i][1]    ; length
            $propsAr[$j][2] =$ar[$i][2]; type
            $vec = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
            $propsAr[$j][3] = $vec[0]   ; val1
            Switch $ar[$i][2]
                Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
                    $propsAr[$j][4] = $vec[1]
                Case Else
                    $propsAr[$j][4] = ''
            EndSwitch
        EndIf
    Next
    ReDim $propsAr[$j+1][5]
    Return $propsAr
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Switch $iType
        Case 1,6    ; $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val',$pValue)
        Case 2      ; $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&']',$pValue)
        Case 3      ; $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val',$pValue)
        Case 4      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 5      ; $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 7      ; $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
            $tvalue = DllStructCreate('byte val['&$ilength&']',$pValue) ; see _GDIPlus_ImageSetPropertyItemEx
;~          $tvalue = DllStructCreate('char val['&$ilength&']',$pValue)
        Case 8      ; $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val',$pValue)
        Case 9      ; $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val',$pValue)
        Case 10     ; $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2',$pValue)
        Case 11     ; $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val',$pValue)
        Case 12     ; $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
    If @error Then Return SetError(@error,0,-1)
    Switch $iType
            Case 5,10   ; $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            Local $aRet[2]
            $aRet[0] = DllStructGetData($tvalue,'val1')
            $aRet[1] = DllStructGetData($tvalue,'val2')
        Case Else
            Local $aRet[1]
            $aRet[0] = DllStructGetData($tvalue,'val')
    EndSwitch
    Return $aRet
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value1,$value2=-1)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag,$tvalue
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEUBYTE,$GDIP_PROPERTYTAGTYPESBYTE
            $tvalue = DllStructCreate('byte val;')
        Case $GDIP_PROPERTYTAGTYPEASCII
            $tvalue = DllStructCreate('char val['&$iLength&'];')
        Case $GDIP_PROPERTYTAGTYPEUSHORT
            $tvalue = DllStructCreate('ushort val;')
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val;')
        Case $GDIP_PROPERTYTAGTYPEURATIONAL
            $tvalue = DllStructCreate('ulong val1;ulong val2;')
        Case $GDIP_PROPERTYTAGTYPEUNDEFINED
            ; undefined, per specification, but may be a long but is sometimes a string
;~          $tag = 'byte val['&$iLength&']' ; causes saving to jpeg to write junk
            $tvalue = DllStructCreate('char val['&$iLength&'];')
        Case $GDIP_PROPERTYTAGTYPESSHORT
            $tvalue = DllStructCreate('short val;')
        Case $GDIP_PROPERTYTAGTYPEULONG
            $tvalue = DllStructCreate('ulong val;')
        Case $GDIP_PROPERTYTAGTYPESRATIONAL
            $tvalue = DllStructCreate('long val1;long val2;')
        Case $GDIP_PROPERTYTAGTYPESFLOAT
            $tvalue = DllStructCreate('float val;')
        Case $GDIP_PROPERTYTAGTYPEDFLOAT
            $tvalue = DllStructCreate('double val;')
    EndSwitch
;~ If @error Then _Msgdebug(@ScriptLineNumber&' create','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEURATIONAL,$GDIP_PROPERTYTAGTYPESRATIONAL
            DllStructSetData($tvalue,1,$value1)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val1','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
            DllStructSetData($tvalue,2,$value2)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val2','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
        Case Else
            DllStructSetData($tvalue,1,$value1)
;~ If @error Then _Msgdebug(@ScriptLineNumber&' val','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))
;~ If @error Then _Msgdebug(@ScriptLineNumber&' pvalue','@error,$id,$ilength,$iType,$value1,$value2',@error,Hex($id,4),$ilength,$iType,$value1,$value2)

    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    DllStructGetPtr($tProp))
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Return $aResult[0] = 0
EndFunc

Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

cDebug.au3 (which is commented out) is my variable-dump library, which can be found here .

The screenshot below shows the values of the first 27 properties as zeros (which is wrong). _GDIPlus_ImageSaveToFile() worked as expected.

Display 1st 27 0, 30 up OK File save 916K, GDIP status 0.png

The screenshot below shows the values of all properties correctly in _ArrayDisplay(). _GDIPlus_ImageSaveToFile() worked as expected.

Display OK File size 916K GDIP status 0.png

The screenshot below shows the values of the first 27 properties OK in _ArrayDisplay() and most of properties 30 to 63 as zeroes when they should be non-zero. The output file size as 36K (which is wrong) and GDIP Status as 7 (Win32 error).

Display first 27 OK 30 up 0 file save 36K GDIP status Win32 error.png

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

I have found an error in my understanding of how EXIF data is stored: for example, type $GDIP_PROPERTYTAGTYPEUSHORT (3) indicates that pValue points to an array of unsigned shorts. So there may not be an error in memory allocation by DllStructCreate().


Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

I have modified the script, recognizing that most of the metadata is stored in a jpeg as arrays. This version abends at run-time with Variable used without being declared on $ouFilspc despite Au3Check not finding any errors, and $oufilspc being declared as Local a few lines before.

Suggestions are most welcome.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
;~ #include "..\cDebug.au3"

; Property Item structure
Global Const $tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

; Image property types constants
; Ref: https://www.media.mit.edu/pia/Research/deepview/exif.html
Global Const $GDIP_PROPERTYTAGTYPEUBYTE = 1
Global Const $GDIP_PROPERTYTAGTYPEASCII = 2
Global Const $GDIP_PROPERTYTAGTYPEUSHORT = 3
Global Const $GDIP_PROPERTYTAGTYPEULONG = 4
Global Const $GDIP_PROPERTYTAGTYPEURATIONAL = 5
Global Const $GDIP_PROPERTYTAGTYPESBYTE = 6
Global Const $GDIP_PROPERTYTAGTYPEUNDEFINED = 7
Global Const $GDIP_PROPERTYTAGTYPESSHORT = 8
Global Const $GDIP_PROPERTYTAGTYPESLONG = 9
Global Const $GDIP_PROPERTYTAGTYPESRATIONAL = 10
Global Const $GDIP_PROPERTYTAGTYPESFLOAT = 11
Global Const $GDIP_PROPERTYTAGTYPEDFLOAT = 12

Global $gId

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()

;~  Local $hImage = _GDIPlus_ImageLoadFromFile('H:\b\1.jpg')
    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
;~  Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\pergola photo - Copy.JPG')
    Local $propsAr = GetProperties($hImage)

Local $dAr = $propsAr
For $i = 0 To UBound($propsAr,1)-1
    $dAr[$i][0] = Hex($propsAr[$i][0],4)
    Switch True
        Case IsArray($propsAr[$i][3])
            Local $t='',$vec = $propsAr[$i][3]
            For $j = 0 To UBound($vec)-1
                $t &= '|'&$vec[$j]
            Next
            $t = StringMid($t,2)
            $dAr[$i][3] = $t
    EndSwitch
Next
_ArrayDisplay($dAr,'At line '&@ScriptLineNumber,'',0,Default,'id|length|type|value')

    For $i = 0 To UBound($propsAr,1)-1
        If Not _GDIPlus_ImageSetPropertyItemEx($hImage,$propsAr[$i][0],$propsAr[$i][1], _
            $propsAr[$i][2],$propsAr[$i][3]) Then
            ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed'&@CRLF)
        EndIf
    Next
    Local $date = '2017:12:09 14:01:23'
    If Not _GDIPlus_ImageSetPropertyItemEx($hImage,0x9003,20,2,$date) Then

    Local $ouFilspc = 'H:\b\2.jpg'
    _GDIPlus_ImageSaveToFile($hImage,$ouFilspc)
    If @error Then MsgBox(0,'_GDIPlus_ImageSaveToFile','@error '&@error&' @extended '&@extended)
        ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed'&@CRLF)
    EndIf

    _GDIPlus_ImageDispose($hImage)
    Local $vec = FileGetTime($ouFilspc) ; at run time:  $ouFilspc "Variable used without being declared"
    MsgBox(0,@ScriptLineNumber,'Output: file size '&FileGetSize($ouFilspc)& _
        ' bytes at '&StringFormat('%s-%02s-%02s %02s:%02s',$vec[0],$vec[1],$vec[2],$vec[3],$vec[4]))
EndFunc

Func GetProperties($hImage)
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $j=-1,$id,$length,$type,$value
    For $i = 1 To $ar[0][0]
        $length = $ar[$i][1]
        $type = $ar[$i][2]
        $value = $ar[$i][3]
        If Not ($value=0 Or ($type=2 And $length=0)) Then
            $j += 1
            $ar[$j][0] = $ar[$i][0]
            $ar[$j][1] = $length
            $ar[$j][2] = $type
            $ar[$j][3] = $value
        EndIf
    Next
    ReDim $ar[$j+1][4]

    Local $propsAr[$j+1][4]
    For $i = 0 To UBound($ar,1)-1
        $propsAr[$i][0] = $ar[$i][0]    ; id
        $propsAr[$i][1] = $ar[$i][1]    ; length
        $propsAr[$i][2] = $ar[$i][2]    ; type
$gId = $propsAr[$i][0]
        $propsAr[$i][3] = _GDIPlus_ImageGetPropertyItemValue($ar[$i][1],$ar[$i][2],$ar[$i][3])
    Next
    Return $propsAr
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    $aSize = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $aSize[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $aSize[1]

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

; Returns:
;   byte and undefined: binary
;   string              string
;   rational            vector: [n] numerator, [n=1] denominator
;   else                vector
; Vectors are ref 0
Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Local $qels

    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $tvalue = DllStructCreate('byte binary['&$ilength&']',$pValue)
        Case 2      ; ASCII
            $tvalue = DllStructCreate('char str['&$iLength&']',$pValue)
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('ushort valVec['&$qels&']',$pValue)
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('ulong valVec['&$qels&']',$pValue)
        Case 8      ; SSHORT
            $qels = $iLength/4
            $tvalue = DllStructCreate('short valVec['&$qels&']',$pValue)
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('long valVec['&$qels&']',$pValue)
        $tvalue = DllStructCreate('double val',$pValue)
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Local $ret
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            Local $ret =  DllStructGetData($tvalue,'binary')
        Case 2 ; ASCII
            Local $ret = DllStructGetData($tvalue,'str')
        Case Else
            Local $ret[$qels]
            For $i = 0 To $qels-1
                $ret[$i] = DllStructGetData($tvalue,'valVec',$i+1)
            Next
    EndSwitch
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Return $ret
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aSize[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aSize[0] = $aResult[2]
    $aSize[1] = $aResult[3]
    Return $aSize
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func _GDIPlus_ImageSetPropertyItemEx($hImage,$id,$iLength,$iType,$value)
    Local $tProp = DllStructCreate($tagGDIPPROPERTYITEM)
    DllStructSetData($tProp,'id',$id)
    DllStructSetData($tProp,'type',$itype)
    DllStructSetData($tProp,'length',$ilength)
    Local $tag,$tvalue,$qels
    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $tvalue = DllStructCreate('byte binary['&$ilength&']')
        Case 2      ; ASCII
            $tvalue = DllStructCreate('char str['&$iLength&']')
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('ushort valVec['&$qels&']')
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('ulong valVec['&$qels&']')
        Case 8      ; SSHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('short valVec['&$qels&']')
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('long valVec['&($iLength/4)&']')
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('SetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            DllStructSetData($tvalue,'binary',$value)
        Case 2  ; ASCII
            DllStructSetData($tvalue,'str',$value)
        Case Else
            For $i = 0 To $qels-1
                DllStructSetData($tvalue,'valVec',$value[$i],$i+1)
            Next
    EndSwitch
    DllStructSetData($tProp,'pvalue',DllStructGetPtr($tvalue))
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('SetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf

Local $ptr = DllStructGetPtr($tProp)
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetPtr: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipSetPropertyItem", "hwnd", $hImage, "ptr", _
    $ptr)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return $aResult[0] = 0
EndFunc
Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

 


Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

The problem is that $outFilSpc is only declared if this If statement evaluates to true. If it's undeclared, then the If statement failed.

If Not _GDIPlus_ImageSetPropertyItemEx($hImage, 0x9003, 20, 2, $date) Then

        Local $ouFilspc = 'H:\b\2.jpg'
        _GDIPlus_ImageSaveToFile($hImage, $ouFilspc)
        If @error Then MsgBox(0, '_GDIPlus_ImageSaveToFile', '@error ' & @error & ' @extended ' & @extended)
        ConsoleWrite('_GDIPlus_ImageSetPropertyItemEx failed' & @CRLF)
    EndIf

 


If I posted any code, assume that code was written using the latest release version unless stated otherwise. Also, if it doesn't work on XP I can't help with that because I don't have access to XP, and I'm not going to.
Give a programmer the correct code and he can do his work for a day. Teach a programmer to debug and he can do his work for a lifetime - by Chirag Gude
How to ask questions the smart way!

I hereby grant any person the right to use any code I post, that I am the original author of, on the autoitscript.com forums, unless I've specifically stated otherwise in the code or the thread post. If you do use my code all I ask, as a courtesy, is to make note of where you got it from.

Back up and restore Windows user files _Array.au3 - Modified array functions that include support for 2D arrays.  -  ColorChooser - An add-on for SciTE that pops up a color dialog so you can select and paste a color code into a script.  -  Customizable Splashscreen GUI w/Progress Bar - Create a custom "splash screen" GUI with a progress bar and custom label.  -  _FileGetProperty - Retrieve the properties of a file  -  SciTE Toolbar - A toolbar demo for use with the SciTE editor  -  GUIRegisterMsg demo - Demo script to show how to use the Windows messages to interact with controls and your GUI.  -   Latin Square password generator

Share this post


Link to post
Share on other sites

Many thanks. My really stupid error which I created when I added in diagnostic code.


Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites
3 hours ago, c.haslam said:

Many thanks. My really stupid error which I created when I added in diagnostic code.

yep, I have done that a few times myself. i always chuckle when it happens


Earthshine
A beacon in the night
I can raise my eyes to 
Earthshine -- RUSH, Earthshine - Vapor Trails

Share this post


Link to post
Share on other sites

I use single-line Ifs very seldom, because I often get them wrong, as I did above!

There are many alligators in my coding swamp right now, so it is not too surprising that I didn't seem that baby one!


Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

#9 ·  Posted (edited)

I have found a curious situation. Snippet:

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aPropertyItems[1][1], $aResult
;~  Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aSize, $aPropertyItems[1][1], $aResult

    Local $ar = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $ar[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $ar[1]
    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, _
        "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)
    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    For $iI = 1 To $iCount
        $tPropertyItem = DllStructCreate($tagGDIPPROPERTYITEM, $pBuffer)
        $aPropertyItems[$iI][0] = DllStructGetData($tPropertyItem, "id")
        $aPropertyItems[$iI][1] = DllStructGetData($tPropertyItem, "length")
        $aPropertyItems[$iI][2] = DllStructGetData($tPropertyItem, "type")
        $aPropertyItems[$iI][3] = DllStructGetData($tPropertyItem, "pvalue")
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aRet[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aRet[0] = $aResult[2]  ; size in bytes
    $aRet[1] = $aResult[3]  ; number of property items
    Return $aRet
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

When I run the script of which this is a part, $iBuffer is 43763 bytes long and $iCount is 66. The code above, which is from GDIP.7z, assumes that GdipGetAllProperty Items sets pBuffer to point to PropertyItem1, PropertyItem2, ... PropertyItemN. The property item struct is

$tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "ptr pvalue;"           ; pointer to property value

Note the pointer. The length of this struct is 16 bytes. So if the data is arranged in this way, $iBuffer would be 66 x 16 = 1056 bytes! So the data cannot be arranged linearly, with one property item after another.

Scenario 2

What if the tag should be like

$tagGDIPPROPERTYITEM = _
    "uint id;" & _          ; ID of this property
    "ulong length;" & _     ; Length of the property value, in bytes
    "word type;" & _        ; Type of the value, as one of TAG_TYPE_XXX constants
    "byte valueVec["&$iLength&"];"

?

I know that one of the property items is about 37K long, and another 1.1K long. The lengths of id, length and type add up to 12 bytes. The lengths of the other values probably average 5.

37K + 1.1K + (12 + 5)*66 = 39222. This is fairly close to 43763 -- but not close enough to convince me that Scenario 2 is correct.

Also,it is strange that the buffer needs to be an odd number of bytes long, because the length of the struct in Scenario1 above (the one I am using) is an even number of bytes.

So what goes?

Early in my trials, I thought that the data is arranged as in Scenario 2. I don't remember where I got this idea.

If the data is arranged per Scenario 2, why does getting property items work as well as it does?

After a night's sleep, I realized that $iBuffer may be the total size occupied by property items. My script now adds up the lengths. They add up to 42707 bytes. 43763 - 42707 = 1056. 1056 / 16 = 66 which is the number of property items! So $iBuffer is the space occupied by values! It is time to do some more thinking.

This does mean that $tBuffer is larger than it needs to be. It also implies that $tagGDIPPROPERTYITEM is correct.

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

5 hours ago, c.haslam said:

There are many alligators in my coding swamp

:lol: You're lucky your swamp is in your code; mine is between my ears!:D

Not sure whether you've considered this (if so, just ignore this), but be aware of potential member alignment issues within your structs; unless you explicitly align, AutoIt may adjust alignment (implicit align 8) and apply padding. Note in particular (from the Help for DllstructCreate):

Quote

The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.  (...)

If a change of alignment is needed "align" can be use[d] before the first element which that needs to be changed (...)

The alignment of the beginning of a structure is the maximum alignment of any individual member.
Each member within the structure is be placed at its proper alignment as defined in the previous table, which require implicit internal padding, depending on the previous member.

The interplay of these "rules" may yield some unexpected results (which is why I tend to align 1 explicitly). Just my two cents.

;)

Edited by RTFC

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

RTFC,

I found a bug in GDIPlus_ImageGetPropertyItemValue(): for Case 9,10 I had accidentally left in

$tvalue = DllStructCreate('double val',$pValue)

I have removed it.

I tried adding align 1; at the start of all tags in GDIPlus_ImageGetPropertyItemValue() . This did not change the results in _ArrayDisplay().

I tried adding align 1; at the start of $tagGDIPPROPERTYITEM. This crashed with rc:-1073741819

I was had been hopeful that your suggestion would solve the problem.

GDIPlus_ImageGetPropertyItemValue() now looks like this:

; Returns:
;   byte array and undefined:   binary
;   byte scalar                 scalar
;   string                      string
;   rational                    vector: [n] numerator, [n=1] denominator
;   else                        vector
; Vectors are ref 0
Func _GDIPlus_ImageGetPropertyItemValue($iLength, $iType, $pValue)
    Static Local $tvalue
    Local $qels
If $pValue=0 Then
Return 0
EndIf
    Switch $iType
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            $qels = $ilength
If $qels=1 Then
$tvalue = DllStructCreate('align 1;byte binary',$pValue)
Else
            $tvalue = DllStructCreate('align 1;byte binary['&$ilength&']',$pValue)
EndIf
        Case 2      ; ASCII
If $ilength=0 Then
Return ''
EndIf
            $qels = $ilength
            $tvalue = DllStructCreate('align 1;char str['&$iLength&']',$pValue)
        Case 3      ; USHORT
            $qels = $iLength/2
            $tvalue = DllStructCreate('align 1;ushort valVec['&$qels&']',$pValue)
        Case 4,5    ; ULONG,URATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;ulong valVec['&$qels&']',$pValue)
        Case 8      ; SSHORT
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;short valVec['&$qels&']',$pValue)
        Case 9,10   ; SLONG,SRATIONAL
            $qels = $iLength/4
            $tvalue = DllStructCreate('align 1;long valVec['&$qels&']',$pValue)
        Case Else
            MsgBox(0,@ScriptLineNumber,'_GDIPlus_ImageGetPropertyItemValue(): type '&$itype& ' found')
    EndSwitch
Local $err = @error
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    If $err Then Return SetError($err,0,-1)
    Local $ret
    Switch $itype
        Case 1,6,7  ; UBYTE,SBYTE,UNDEFINED
            Local $ret =  DllStructGetData($tvalue,'binary')
        Case 2 ; ASCII
            Local $ret = DllStructGetData($tvalue,'str')
        Case Else
            Local $ret[$qels]
            For $i = 0 To $qels-1
                $ret[$i] = DllStructGetData($tvalue,'valVec',$i+1)
            Next
    EndSwitch
If $err Then
    ConsoleWrite('@error '&$err&@CRLF)
    ConsoleWrite('GetData: id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype&@CRLF)
    MsgBox(0,@ScriptLineNumber,'id '&Hex($gId,4)&' ilength ' &$ilength&' type '&$itype)
EndIf
    Return $ret
EndFunc

Diagnostic code and most of the new code is outdented, for now.

I had been excluding property items for which the value is zero and ASCII property items for which the length is zero in the caller. These cases are now handled in this function.

For now, I am only working on the script through to the array display.

Edited by c.haslam

Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
Share on other sites

There are 3 possibilities for bugs:

  • In my code
  • In a DLLStruct* function
  • In Microsoft's gdip* functions

I have been working on the third possibility.

I have run the script that follows several times. Desk-checking the output to the console has convinced me, at least for now, that Microsoft's GdipGetAllPropertyItems() works consistently. Its return is as advertised, i.e. it is my Scenario 1. This does not rule out the possibility of a memory leak.

That leaves the other two possibilities. A memory leak is possible in both.

It is beyond me as to how the second script below sometimes reports an ASCII string, and sometimes doesn't.

The script used to check GdipGetAllPropertyItems() is below. It reports in the odd format because I needed to be able to find Id's if the alignment was other than 2.

#include <GDIPlus.au3>
#include <Array.au3>
Opt('MustDeclareVars',1)
#include "..\cDebug.au3"

OnAutoItExitRegister(FinishUp)

main()

Func main()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile('H:\temp\AP test data\DSC00824 - Copy.jpg')
    Local $ar = _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
EndFunc

Func _GDIPlus_ImageGetAllPropertyItemsEx($hImage)
    Local $iI, $iCount, $tBuffer, $pBuffer, $iBuffer, $tPropertyItem, $aPropertyItems[1][1], $aResult

    Local $ar = _GDIPlus_ImageGetPropertySize($hImage)
    If @error Then Return SetError(@error, @extended, -1)

    $iBuffer = $ar[0]
    $tBuffer = DllStructCreate("byte[" & $iBuffer & "]")
    $pBuffer = DllStructGetPtr($tBuffer)
    $iCount = $ar[1]
    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, _
        "uint", $iBuffer, "uint", $iCount, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)
    ReDim $aPropertyItems[$iCount + 1][4]
    $aPropertyItems[0][0] = $iCount

    Local $byAr[$iCount*16]
    For $j = 0 To ($iCount-1)*16
        Local $t = DllStructCreate('byte;',$pBuffer+$j)
        $byAr[$j] = DllStructGetData($t,1)
    Next
    For $j = 0 To ($iCount-1)*16
        _consdebug(@ScriptLineNumber,'j,val',($j+1)&'-'&$j,Hex($byAr[$j+1],2)&Hex($byAr[$j],2))
    Next
EndFunc

Func _GDIPlus_ImageGetPropertySize($hImage)
    Local $aRet[2], $aResult

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error, @extended, -1)

    $aRet[0] = $aResult[2]  ; size in bytes
    $aRet[1] = $aResult[3]  ; number of property items
    Return $aRet
EndFunc   ;==>_GDIPlus_ImageGetPropertySize

Func FinishUp()
    _GDIPlus_Shutdown()
EndFunc

There is one strange finding: as mentioned in an earlier post, $tBuffer only needs to be large enough for the 66 property items, so I tried a buffer of 66*16 bytes.  GdipGetAllPropertyItems() returned an error code!

 


Spoiler

CDebug Dumps values of variables including arrays, to a GUI, to the Console, and to the Clipboard

 

Share this post


Link to post
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

  • Similar Content

    • Blaxxun
      By Blaxxun
      #include <GDIPlus.au3> Text2PNG(@DesktopDir & "\MyPNG.png", 0x00FFFFFF) ; Transparent but bold ugly text ;Text2PNG(@DesktopDir & "\MyPNG.png", 0xFFFFFFFF) ; Nice Text but not transparent Func Text2PNG($sFile, $iColor) _GDIPlus_Startup() Local $hImage = _GDIPlus_BitmapCreateFromScan0(50, 25) Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hImage) _GDIPlus_GraphicsClear($hGraphics, $iColor) _GDIPlus_GraphicsDrawString($hGraphics, "Hello", 0, 0, "Arial", 12, 0) _GDIPlus_ImageSaveToFile($hImage, $sFile) _GDIPlus_GraphicsDispose($hGraphics) _GDIPlus_BitmapDispose($hImage) _GDIPlus_Shutdown() EndFunc ;==>Text2PNG  
      Hello ladys and gentlemen,
      I have this piece of code and it does what it's supposed to do.
      I want to use it to create an image sequence with frame/image information for compositing overlay.
      However, when i use a transparent background the text starts to look kinda bold or fat.
      I dont know what causes this ugly effect.
      Is there a way to avoid this effect somehow?
      Thanks!
       
       
    • UEZ
      By UEZ
      I saw this code here: http://zoomquilt.org/ and here: http://arkadia.xyz and thought how this can be implemented in AutoIt. Here the results.
       
      The Zoomquilt:
      ;coded by UEZ build 2018-01-10, idea and images taken from http://zoomquilt.org/ ;thanks to spudw2k for the MouseZoom function #pragma compile(Icon, "c:\Program Files (x86)\AutoIt3\Icons\au3.ico") #AutoIt3Wrapper_Run_Au3Stripper=y #Au3Stripper_Parameters=/so /pe /rm #AutoIt3Wrapper_Run_After=del /f /q "%scriptdir%\%scriptfile%_stripped.au3" #include <GDIPlus.au3> #include <GuiConstantsEx.au3> #include <WindowsConstants.au3> _GDIPlus_Startup() Global $hGUI, $iFPS = 0, $iShowFPS = 0, $bExit, $iW, $iH Global Const $sTitle = "GDI Image Zoom v2.1.2 coded by UEZ" AutoItSetOption("GUIOnEventMode", 1) Downloader() GDIPlus_ZoomImage() AutoItSetOption("GUIOnEventMode", 0) _GDIPlus_Shutdown() Func GDIPlus_ZoomImage($bMultimonitor = False) $bExit = False Local $i, $aImages[46], $hImage, $hObj ConsoleWrite("Loading images from local disk..." & @CRLF) Local $fTimer = TimerInit() For $i = 0 To UBound($aImages) - 1 $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\TheZoomquilt" & StringFormat("%02i.jpg", $i)) $aImages[$i] = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) _GDIPlus_ImageDispose($hImage) Next ConsoleWrite(UBound($aImages) & " images loaded in " & TimerDiff($fTimer) & " ms" & @CRLF) Local $tDim = DllStructCreate($tagBITMAP) DllCall("GDI32.dll", 'int', 'GetObject', 'int', $aImages[0], 'int', DllStructGetSize($tDim), 'ptr', DllStructGetPtr($tDim)) $iW = $tDim.bmWidth $iH = $tDim.bmHeight Local $hFullScreen = WinGetHandle("[TITLE:Program Manager;CLASS:Progman]"), $aFullScreen[4], $iW_Dt, $iH_Dt $aFullScreen = WinGetPos($hFullScreen) If $bMultimonitor Then $iW_Dt = $aFullScreen[2] $iH_Dt = $aFullScreen[3] Else $iW_Dt = @DesktopWidth $iH_Dt = @DesktopHeight $aFullScreen[0] = "" $aFullScreen[1] = "" EndIf $hGUI = GUICreate($sTitle, $iW_Dt, $iH_Dt, $aFullScreen[0], $aFullScreen[1], $WS_POPUP, $WS_EX_TOPMOST) GUISetState(@SW_SHOW, $hGUI) GUISetCursor(16, 1) ;create canvas elements Local Const $hDC = _WinAPI_GetDC($hGUI) Local Const $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW_Dt, $iH_Dt) Local Const $hDC_backbuffer = _WinAPI_CreateCompatibleDC($hDC) Local Const $DC_obj = _WinAPI_SelectObject($hDC_backbuffer, $hHBitmap) Local Const $hMemDC = _WinAPI_CreateCompatibleDC($hDC) _WinAPI_SetStretchBltMode($hDC_backbuffer, $STRETCH_DELETESCANS) Local Const $hCanvas = _GDIPlus_GraphicsCreateFromHDC($hDC_backbuffer) _GDIPlus_GraphicsSetSmoothingMode($hCanvas, $GDIP_SMOOTHINGMODE_HIGHQUALITY) _GDIPlus_GraphicsSetPixelOffsetMode($hCanvas, $GDIP_PIXELOFFSETMODE_HIGHQUALITY) Local Const $hBrush_Clr = _GDIPlus_BrushCreateSolid(0xFF000000), _ $hBrush_FPS = _GDIPlus_BrushCreateSolid(0xFFFFFFFF), _ $hFormat_FPS = _GDIPlus_StringFormatCreate(), _ $hFamily_FPS = _GDIPlus_FontFamilyCreate("Arial"), _ $hFont_FPS = _GDIPlus_FontCreate($hFamily_FPS, 8), _ $tLayout_FPS = _GDIPlus_RectFCreate(0, 0, 130, 16) $iFPS = 0 GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit_About") AdlibRegister("CalcFPS", 1000) Local $a[3], $b = 1, $c, $x, $e, $y, $w, $h, $w2 = $iW_Dt / 2, $h2 = $iH_Dt / 2, $q, $r If $iW_Dt > 1.5 * $iH_Dt Then $q = $iW_Dt $r = 0.75 * $iW_Dt Else $q = 1.5 * $iH_Dt $r = 0.75 * $iH_Dt EndIf Do For $e = 0 To 2 $a[$e] = $aImages[Mod(Floor($b) + $e, UBound($aImages))] Next $c = 2^(Mod($b, 1)) For $e = 0 To 2 $x = $w2 - $q / 2 * $c $y = $h2 - $r / 2 * $c $w = $q * $c $h = $r * $c $hObj = _WinAPI_SelectObject($hMemDC, $a[$e]) _WinAPI_StretchBlt($hDC_backbuffer, $x, $y, $w, $h, $hMemDC, 0, 0, $iW, $iH, $SRCCOPY) $c *= 0.5 Next $b += MouseZoom() IF $b < 0 Then $b = UBound($aImages) - $b _GDIPlus_GraphicsDrawStringEx($hCanvas, "FPS: " & $iShowFPS & " @ " & $iW_Dt & "x" & $iH_Dt & " px", $hFont_FPS, $tLayout_FPS, $hFormat_FPS, $hBrush_FPS) _WinAPI_BitBlt($hDC, 0, 0, $iW_Dt, $iH_Dt, $hDC_backbuffer, 0, 0, $SRCCOPY) $iFPS += 1 If $bExit Then ExitLoop Until Not Sleep(0) AdlibUnRegister("CalcFPS") ;release resources _GDIPlus_FontDispose($hFont_FPS) _GDIPlus_FontFamilyDispose($hFamily_FPS) _GDIPlus_StringFormatDispose($hFormat_FPS) _GDIPlus_BrushDispose($hBrush_Clr) _GDIPlus_BrushDispose($hBrush_FPS) _GDIPlus_GraphicsDispose($hCanvas) _WinAPI_SelectObject($hMemDC, $hObj) _WinAPI_DeleteDC($hMemDC) _WinAPI_SelectObject($hDC_backbuffer, $DC_obj) _WinAPI_DeleteDC($hDC_backbuffer) _WinAPI_DeleteObject($hHBitmap) _WinAPI_ReleaseDC($hGUI, $hDC) For $i = 0 To UBound($aImages) - 1 _WinAPI_DeleteObject($aImages[$i]) Next GUIDelete($hGUI) EndFunc ;==>GDIPlus_ZoomImage Func MouseZoom() ;https://www.arduino.cc/reference/en/language/functions/math/map/ Local $iInput = MouseGetPos(1), $iInMin = 0, $iInMax = @DesktopHeight, $iOutMin = 1, $iOutMax = -1, $iStep = 0.025 Return (($iInput - $iInMin) * ($iOutMax - $iOutMin) / ($iInMax - $iInMin) + $iOutMin) * $iStep EndFunc ;MouseZoom() Func _Exit_About() $bExit = True EndFunc ;==>_Exit_About Func CalcFPS() ;display FPS $iShowFPS = $iFPS $iFPS = 0 EndFunc ;==>CalcFPS Func Downloader() Local $i, $A = StringSplit("FUjD9hf gbHhxTR 8YyzJdR xP3aNkR 2Qi4fQr E6pW5Ky zmtWIBF Af7LtYp TuXy30d 3nKGLr2 hNoWscB mSBvv3K f4wJ70e mIt9XmM M4TkAyh P4L4qhd hNM6bTv VoT8JXM jqcGH0B DYVoN8n bOPQkOI NeaTfJ1 18ppMNr FZ3d8Jv HsoX2RP mjv4kzI 6rpJbef pySKauq WjNQYRV Ffooo8y Xei5XfD T5A415r LiV0VNB nGcwiO4 b1Gdjjy GE828iy eSQ7SLe 1mPyGgL GNtwJIr KxBlU7E aKXhms5 9Quu2wu Y07quDf r0yC5Qa 273fCkD 2wMyCUw FUjD9hf", " ", 2) Local $sURL For $i = 0 To UBound($A) - 1 If Not FileExists(@ScriptDir & "\Images\TheZoomquilt" & StringFormat("%02i.jpg", $i)) Then If Not FileExists(@ScriptDir & "\Images") Then DirCreate(@ScriptDir & "\Images") $sURL = "http://imgur.com/" & $A[Mod(20 + $i, 46)] & ".jpg" ConsoleWrite("Downloading " & $sURL & ": " & InetGet($sURL, @ScriptDir & "\Images\TheZoomquilt" & StringFormat("%02i.jpg", $i), 8) & " bytes" & @CRLF) ToolTip("Downloading images...Please wait! -> " & $i + 1 & " / " & UBound($A), MouseGetPos(0), MouseGetPos(1)) EndIf Next ToolTip("") EndFunc ;==>Downloader  
       
      Arkadia:
      ;coded by UEZ build 2018-01-10, idea and images taken from http://arkadia.xyz ;thanks to spudw2k for the MouseZoom function #pragma compile(Icon, "c:\Program Files (x86)\AutoIt3\Icons\au3.ico") #AutoIt3Wrapper_Run_Au3Stripper=y #Au3Stripper_Parameters=/so /pe /rm #AutoIt3Wrapper_Run_After=del /f /q "%scriptdir%\%scriptfile%_stripped.au3" #include <GDIPlus.au3> #include <GuiConstantsEx.au3> #include <WindowsConstants.au3> _GDIPlus_Startup() Global $hGUI, $iFPS = 0, $iShowFPS = 0, $bExit, $iW, $iH Global Const $sTitle = "GDI Image Zoom v2.2.1 coded by UEZ" AutoItSetOption("GUIOnEventMode", 1) Downloader() GDIPlus_ZoomImage() AutoItSetOption("GUIOnEventMode", 0) _GDIPlus_Shutdown() Func GDIPlus_ZoomImage($bMultimonitor = False) $bExit = False Local $i, $aImages[49], $hImage, $hObj ConsoleWrite("Loading images from local disk..." & @CRLF) Local $fTimer = TimerInit() For $i = 0 To UBound($aImages) - 1 $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\Arkadia" & $i & ".jpg") $aImages[$i] = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) _GDIPlus_ImageDispose($hImage) Next ConsoleWrite(UBound($aImages) & " images loaded in " & TimerDiff($fTimer) & " ms" & @CRLF) Local $tDim = DllStructCreate($tagBITMAP) DllCall("GDI32.dll", "int", "GetObject", "int", $aImages[0], "int", DllStructGetSize($tDim), "ptr", DllStructGetPtr($tDim)) $iW = $tDim.bmWidth $iH = $tDim.bmHeight Local $hFullScreen = WinGetHandle("[TITLE:Program Manager;CLASS:Progman]"), $aFullScreen[4], $iW_Dt, $iH_Dt $aFullScreen = WinGetPos($hFullScreen) If $bMultimonitor Then $iW_Dt = $aFullScreen[2] $iH_Dt = $aFullScreen[3] Else $iW_Dt = @DesktopWidth $iH_Dt = @DesktopHeight $aFullScreen[0] = "" $aFullScreen[1] = "" EndIf $hGUI = GUICreate($sTitle, $iW_Dt, $iH_Dt, $aFullScreen[0], $aFullScreen[1], $WS_POPUP, $WS_EX_TOPMOST) GUISetState(@SW_SHOW, $hGUI) GUISetCursor(16, 1) ;create canvas elements Local Const $hDC = _WinAPI_GetDC($hGUI) Local Const $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iW_Dt, $iH_Dt) Local Const $hDC_backbuffer = _WinAPI_CreateCompatibleDC($hDC) Local Const $DC_obj = _WinAPI_SelectObject($hDC_backbuffer, $hHBitmap) Local Const $hMemDC = _WinAPI_CreateCompatibleDC($hDC) _WinAPI_SetStretchBltMode($hDC_backbuffer, $STRETCH_DELETESCANS) Local Const $hCanvas = _GDIPlus_GraphicsCreateFromHDC($hDC_backbuffer) _GDIPlus_GraphicsSetSmoothingMode($hCanvas, $GDIP_SMOOTHINGMODE_HIGHQUALITY) _GDIPlus_GraphicsSetPixelOffsetMode($hCanvas, $GDIP_PIXELOFFSETMODE_HIGHQUALITY) Local Const $hBrush_Clr = _GDIPlus_BrushCreateSolid(0xFF000000), _ $hBrush_FPS = _GDIPlus_BrushCreateSolid(0xFFFFFFFF), _ $hFormat_FPS = _GDIPlus_StringFormatCreate(), _ $hFamily_FPS = _GDIPlus_FontFamilyCreate("Arial"), _ $hFont_FPS = _GDIPlus_FontCreate($hFamily_FPS, 8), _ $tLayout_FPS = _GDIPlus_RectFCreate(0, 0, 130, 16) $iFPS = 0 GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit_About") AdlibRegister("CalcFPS", 1000) Local $a[3], $b = 1, $c, $x, $e, $y, $w, $h, $w2 = $iW_Dt / 2, $h2 = $iH_Dt / 2, $q, $r If $iW_Dt > 1.5 * $iH_Dt Then $q = $iW_Dt $r = 0.75 * $iW_Dt Else $q = 1.5 * $iH_Dt $r = 0.75 * $iH_Dt EndIf Do For $e = 0 To 2 $a[$e] = $aImages[Mod(Floor($b) + $e, UBound($aImages))] Next $c = 2^(Mod($b, 1)) For $e = 0 To 2 $x = $w2 - $q / 2 * $c $y = $h2 - $r / 2 * $c $w = $q * $c $h = $r * $c $hObj = _WinAPI_SelectObject($hMemDC, $a[$e]) _WinAPI_StretchBlt($hDC_backbuffer, $x, $y, $w, $h, $hMemDC, 0, 0, $iW, $iH, $SRCCOPY) $c *= 0.5 Next $b += MouseZoom() IF $b < 0 Then $b = UBound($aImages) - $b _GDIPlus_GraphicsDrawStringEx($hCanvas, "FPS: " & $iShowFPS & " @ " & $iW_Dt & "x" & $iH_Dt & " px", $hFont_FPS, $tLayout_FPS, $hFormat_FPS, $hBrush_FPS) _WinAPI_BitBlt($hDC, 0, 0, $iW_Dt, $iH_Dt, $hDC_backbuffer, 0, 0, $SRCCOPY) $iFPS += 1 If $bExit Then ExitLoop Until Not Sleep(0) AdlibUnRegister("CalcFPS") ;release resources _GDIPlus_FontDispose($hFont_FPS) _GDIPlus_FontFamilyDispose($hFamily_FPS) _GDIPlus_StringFormatDispose($hFormat_FPS) _GDIPlus_BrushDispose($hBrush_Clr) _GDIPlus_BrushDispose($hBrush_FPS) _GDIPlus_GraphicsDispose($hCanvas) _WinAPI_SelectObject($hMemDC, $hObj) _WinAPI_DeleteDC($hMemDC) _WinAPI_SelectObject($hDC_backbuffer, $DC_obj) _WinAPI_DeleteDC($hDC_backbuffer) _WinAPI_DeleteObject($hHBitmap) _WinAPI_ReleaseDC($hGUI, $hDC) For $i = 0 To UBound($aImages) - 1 _WinAPI_DeleteObject($aImages[$i]) Next GUIDelete($hGUI) EndFunc ;==>GDIPlus_ZoomImage Func MouseZoom() ;https://www.arduino.cc/reference/en/language/functions/math/map/ Local $iInput = MouseGetPos(1), $iInMin = 0, $iInMax = @DesktopHeight, $iOutMin = 1, $iOutMax = -1, $iStep = 0.025 Return (($iInput - $iInMin) * ($iOutMax - $iOutMin) / ($iInMax - $iInMin) + $iOutMin) * $iStep EndFunc ;MouseZoom() Func _Exit_About() $bExit = True EndFunc ;==>_Exit_About Func CalcFPS() ;display FPS $iShowFPS = $iFPS $iFPS = 0 EndFunc ;==>CalcFPS Func Downloader() Local $sURL, $i For $i = 0 To 48 If Not FileExists(@ScriptDir & "\Images\Arkadia" & $i & ".jpg") Then If Not FileExists(@ScriptDir & "\Images") Then DirCreate(@ScriptDir & "\Images") $sURL = "http://arkadia.xyz/images/arkadia" & $i & ".jpg" ConsoleWrite("Downloading " & $sURL & ": " & InetGet($sURL, @ScriptDir & "\Images\Arkadia" & $i & ".jpg", 8) & " bytes" & @CRLF) ToolTip("Downloading images...Please wait! -> " & $i + 1 & " / " & $i, MouseGetPos(0), MouseGetPos(1)) EndIf Next ToolTip("") EndFunc ;==>Downloader  
      The missing images will be download and saved to script dir in folder images. Due to internal integer format of the GDI functions the screen is little bit wobbling.
       
      Happy watching. 
    • youtuber
      By youtuber
      $hImage = _GDIPlus_ImageLoadFromFile($aImageSave) $X1 = _GDIPlus_ImageGetWidth($hImage) $Y1 = _GDIPlus_ImageGetHeight($hImage) $X2 = _GDIPlus_ImageGetWidth($hImage) $Y2 = _GDIPlus_ImageGetHeight($hImage) $hGraphic = _GDIPlus_ImageGetGraphicsContext ($hImage) $hGraphic = _GDIPlus_GraphicsCreateFromHWND($Form1) ;$PicPreview = GUICtrlCreatePic("", 24, 152, 572, 268) _GDIPlus_GraphicsDrawImage ($hGraphic, 24, 152, 572, 268);? I have the same size and relative coords to $PicPreview in the image placement form1 I want to be 
       
      Other Responsibility Is there a problem with _GDIPlus code sorting?
      #include <GDIPlus.au3> #include <GuiConstantsEx.au3> #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> Global $Form1, $hImage, $hGraphic, $OpenPictures, $PicPreview _GDIPlus_StartUp() $Form1 = GUICreate("Form1", 615, 438, 374, 347) $PicPreview = GUICtrlCreatePic("", 24, 152, 572, 268) $ButtonPreview = GUICtrlCreateButton("Preview", 520, 112, 75, 25) $InputImageDir = GUICtrlCreateInput("", 64, 48, 177, 21) $ButtonOpen = GUICtrlCreateButton("...", 256, 48, 75, 25) $ButtonWatermark = GUICtrlCreateButton("Watermark Add", 256, 112, 80, 25) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_ImageDispose($hImage) _GDIPlus_ShutDown() Exit Case $ButtonOpen $OpenPictures = FileOpenDialog("Where are the pictures?", @ScriptDir & "\", "Picture format (*.jpg;*.bmp;*.png)", $FD_FILEMUSTEXIST + $FD_MULTISELECT) If Not $OpenPictures Then MsgBox(64, "İnfo","No Files Selected!",5) Else GUICtrlSetData($InputImageDir,$OpenPictures) EndIf Case $ButtonWatermark $aImageLoad = $OpenPictures $aImageSave = @ScriptDir & "\Save.png" $aImageLogo = @ScriptDir & "\Logo.png" _Watermark($aImageSave, $aImageLoad, $aImageLogo) Case $ButtonPreview $hImage = _GDIPlus_ImageLoadFromFile($aImageSave) $X1 = _GDIPlus_ImageGetWidth($hImage) $Y1 = _GDIPlus_ImageGetHeight($hImage) $X2 = _GDIPlus_ImageGetWidth($hImage) $Y2 = _GDIPlus_ImageGetHeight($hImage) $hGraphic = _GDIPlus_ImageGetGraphicsContext ($hImage) $hGraphic = _GDIPlus_GraphicsCreateFromHWND($Form1) ;$PicPreview = GUICtrlCreatePic("", 24, 152, 572, 268) _GDIPlus_GraphicsDrawImage ($hGraphic, 24, 152, 572, 268) EndSwitch WEnd Func _Watermark($sFile2, $sFile, $sLogo) Local $hImage1, $hImage2, $hGraphic _GDIPlus_Startup () $hImage1 = _GDIPlus_ImageLoadFromFile ($sFile) ; image $X1 = _GDIPlus_ImageGetWidth ($hImage1) $Y1 = _GDIPlus_ImageGetHeight ($hImage1) $hImage2 = _GDIPlus_ImageLoadFromFile ($sLogo) ; logo $X2 = _GDIPlus_ImageGetWidth ($hImage2) $Y2 = _GDIPlus_ImageGetHeight ($hImage2) $hGraphic = _GDIPlus_ImageGetGraphicsContext ($hImage1) _GDIPlus_GraphicsDrawImage ($hGraphic, $hImage2, 20, $Y1-$Y2-30) _GDIPlus_ImageSaveToFile ($hImage1, $sFile2) ; image watermarked _GDIPlus_ImageDispose ($hImage1) _GDIPlus_ImageDispose ($hImage2) _GDIPlus_ShutDown () EndFunc ;==>_Watermark  
    • tcurran
      By tcurran
      Here are two functions to provide pixel-accurate height and width dimensions for a given string.
      The more commonly-used _GDIPlus_GraphicsMeasureString built-in UDF is problematic because it returns the width padded by roughly one en-space (for reasons related to the various ways Windows produces anti-aliased fonts).
      These are AutoIt translations of Pierre Arnaud's C# functions, described in his CodeProject article "Bypass Graphics.MeasureString limitations"
      The first is an all-purpose version that takes a window handle, string, font family, font size (in points), style, and (optionally) width of the layout column (in pixels) as parameters.
      The second, more efficient version is intended for applications where GDI+ fonts are already in use, and takes handles to the existing graphics context, string, font, layout and format as parameters.
      Both functions return a two-row array with the exact width [0] and height [1] of the string (in pixels).
      EDIT: (Note that some of the same anti-aliasing measurement issues still apply. I did my best to work around them, but the output of the function may still be off by a pixel or two. Buyer beware.)
      #include <GDIPlus.au3> #include <GUIConstantsEx.au3> ; #FUNCTION# ==================================================================================================================== ; Name ..........: _StringInPixels ; Description ...: Returns a pixel-accurate height and width for a given string using a given font, style and size. ; Syntax ........: _StringInPixels($hGUI, $sString, $sFontFamily, $fSize, $iStyle[, $iColWidth = 0]) ; Parameters ....: $hGUI - Handle to the window. ; $sString - The string to be measured. ; $sFontFamily - Full name of the font to use. ; $fSize - Font size in points (half-point increments). ; $iStyle - Combination of 0-normal, 1-bold, 2-italic, 4-underline, 8-strikethrough ; $iColWidth - [optional] If word-wrap is desired, column width in pixels ; Return values .: 2-row array. [0] is width in pixels; [1] is height in pixels. ; Author ........: Tim Curran; adapted from Pierre Arnaud's C# function ; Modified ......: ; Remarks .......: This version is longer and less efficient but works for all purposes. ; Related .......: <https://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations> ; Link ..........: ; Example .......: Example-StringInPixels.au3 ; =============================================================================================================================== #include <GDIPlus.au3> #include <GUIConstantsEx.au3> Func _StringInPixels($hGUI, $sString, $sFontFamily, $fSize, $iStyle, $iColWidth = 0) _GDIPlus_Startup() Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI) ;Create a graphics object from a window handle Local $aRanges[2][2] = [[1]] $aRanges[1][0] = 0 ;Measure first char (0-based) $aRanges[1][1] = StringLen($sString) ;Region = String length Local $hFormat = _GDIPlus_StringFormatCreate() Local $hFamily = _GDIPlus_FontFamilyCreate($sFontFamily) Local $hFont = _GDIPlus_FontCreate($hFamily, $fSize, $iStyle) _GDIPlus_GraphicsSetTextRenderingHint($hGraphic, $GDIP_TEXTRENDERINGHINT_ANTIALIASGRIDFIT) _GDIPlus_StringFormatSetMeasurableCharacterRanges($hFormat, $aRanges) ;Set ranges Local $aWinClient = WinGetClientSize($hGUI) If $iColWidth = 0 Then $iColWidth = $aWinClient[0] Local $tLayout = _GDIPlus_RectFCreate(10, 10, $iColWidth, $aWinClient[1]) Local $aRegions = _GDIPlus_GraphicsMeasureCharacterRanges($hGraphic, $sString, $hFont, $tLayout, $hFormat) ;get array of regions Local $aBounds = _GDIPlus_RegionGetBounds($aRegions[1], $hGraphic) Local $aWidthHeight[2] = [$aBounds[2], $aBounds[3]] ; Clean up resources _GDIPlus_FontDispose($hFont) _GDIPlus_RegionDispose($aRegions[1]) _GDIPlus_FontFamilyDispose($hFamily) _GDIPlus_StringFormatDispose($hFormat) _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_Shutdown() Return $aWidthHeight EndFunc ;==>_StringInPixels ; #FUNCTION# ==================================================================================================================== ; Name ..........: _StringInPixels_gdip ; Description ...: Returns a pixel-accurate height and width for a given string using a GDI+ font, layout and format ; Syntax ........: _StringInPixels_gdip($hGraphic, $sString, $hFont, $tLayout, $hFormat) ; Parameters ....: $hGraphic - Handle to a GDI+ graphics object. ; $sString - The string to be measured. ; $hFont - Handle to a GDI+ font. ; $tLayout - A $tagGDIPRECTF structure that bounds the string. ; $hFormat - Handle to a GDI+ string format. ; Return values .: 2-row array. [0] is width in pixels; [1] is height in pixels. ; Author ........: Tim Curran; adapted from Pierre Arnaud's C# function ; Modified ......: ; Remarks .......: This much more efficient version is for use with GDI+ fonts ; Related .......: ; Link ..........: <https://www.codeproject.com/Articles/2118/Bypass-Graphics-MeasureString-limitations> ; Example .......: Example-StringInPixels.au3 ; =============================================================================================================================== #include <GDIPlus.au3> #include <GUIConstantsEx.au3> Func _StringInPixels_gdip($hGraphic, $sString, $hFont, $tLayout, $hFormat) Local $aRanges[2][2] = [[1]] $aRanges[1][0] = 0 ;Measure first char (0-based) $aRanges[1][1] = StringLen($sString) ;Region = String length _GDIPlus_GraphicsSetTextRenderingHint($hGraphic, $GDIP_TEXTRENDERINGHINT_CLEARTYPEGRIDFIT) _GDIPlus_StringFormatSetMeasurableCharacterRanges($hFormat, $aRanges) ;Set ranges Local $aRegions = _GDIPlus_GraphicsMeasureCharacterRanges($hGraphic, $sString, $hFont, $tLayout, $hFormat) ;get array of regions Local $aBounds = _GDIPlus_RegionGetBounds($aRegions[1], $hGraphic) Local $aWidthHeight[2] = [$aBounds[2], $aBounds[3]] _GDIPlus_RegionDispose($aRegions[1]) Return $aWidthHeight EndFunc ;==>_StringInPixels_gdip  
      _StringInPixels.au3
      Example-StringInPixels.au3