Jump to content

Rethinking _GDIPlus_Image*PropertyItem* functions


c.haslam
 Share

Recommended Posts

I think that _GDIPlus_ImageGetPropertyItem should return a Property Item that can readily be set as a property of another image. Why would one want to do this? A script might (as one of mine does) edit an image of a .jpg file then write the result to a .jpg file. In editing it, in my case, the number of horizontal and vertical pixels change, and the date/time edited should be set as a property. All other property items remain the same. Other GDI+ users may change other properties.

So an approach is to copy all the property items from image1 to image2, then update a few properties.

M$ provides a Property Item class (id, length, pointer to an array of values) but _GDIPlus_ImageGetPropertyItem returns something different.

I think that, wherever reasonable, UDFs that are wrappers for M$ methods should work like M$'s methods. _GDIPlus_ImageGetPropertyItem does not do this:

  • While M$ returns 2 values for a value that is a ratio of 2 numbers,  _GDIPlus_ImageGetPropertyItem returns numerator/denominator as a single value (often a Double). _GDIPlus_ImageSetPropertyItem (when included in GDIPlus.au3) will be unable to set ratio property items properly because it cannot know what the numerator and denominator are. So copying such property items will not work properly.
  • M$ has a Void* buffer where the buffer is an array of values but _GDIPlus_ImageGetPropertyItem() returns the values in the same 1-d array as id, length and type. M$'s approach produces a 1-d array with the same number of elements for all property items while _GDIPlus_ImageGetPropertyItem produces a 1-d array with various numbers of elements. To me, this is not good programming practice. For a photo from a Sony camera, the worst case has 1-d array with 67 elements. When combined into a 2-d array with the property items of all properties, there are 66 rows and 67 columns, with many elements not used.

So I suggest that _GDIPlus_ImageGetPropertyItem look like this:

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                                    [0] - identifier
;                                    [1] - size, in bytes, of the value array
;                                    [2] - type of value(s) in the value array
;                                    [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
    Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(@error, @extended, -1)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
        $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr pvalue;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "pvalue")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem,'type')
    Local $tValues, $iValues
    Switch $type
        Case 2 ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case 3 ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case 4, 5 ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case 9, 10 ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem,'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $aVals[$iValues]
    If $type=2 Or $type=7 Then  ; ASCII string or undefined
        $aVals[0] = DllStructGetData($tValues,1)
    Else
        For $i = 0 To $iValues-1
            $aVals[$i] = DllStructGetData($tValues,1,$i+1)
        Next
    EndIf
    $aRet[3] = $aVals
    Return $aRet
EndFunc

And here is an example:

#include <GDIPlus.au3>
#include <Array.au3>

Example()

Func Example()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile(RegRead((@AutoItX64 = True ? "HKLM\SOFTWARE\Wow6432Node\AutoIt v3\AutoIt" : "HKLM\SOFTWARE\AutoIt v3\AutoIt"), "InstallDir") & "\Examples\GUI\Torus.png")
    If @error Then
        _GDIPlus_Shutdown()
        MsgBox(16, "", "An error has occured - unable to load image!", 30)
        Return False
    EndIf
    Local $ar = _GDIPlus_ImageGetPropertyIdList($hImage)
    Local $vPropNbrs[UBound($ar,1)-1]
    ; Extract ID numbers
    For $i = 1 To UBound($ar,1)-1
        $vPropNbrs[$i-1] = $ar[$i][0]
    Next

    ; Get all property items
    Local $aPropItems[UBound($vPropNbrs)][4],$vPropItem
    For $i = 0 To UBound($vPropNbrs)-1
        $vPropItem = cGDIPlus_ImageGetPropertyItem($hImage,$vPropNbrs[$i])
        For $j = 0 To 3
            $aPropItems[$i][$j] = $vPropItem[$j]
        Next
    Next

    ; Collapse values arrays so _ArrayDisplay can display them
    Local $ar = $aPropItems
    For $i = 0 To UBound($aPropItems,1)-1
        $ar[$i][0] = '0x'&Hex($ar[$i][0],4)
        $ar[$i][3] = ''
        For $j = 0 To UBound($aPropItems[$i][3])-1
            $ar[$i][3] &= ($aPropItems[$i][3])[$j]&'|'
        Next
        $ar[$i][3] = StringTrimRight($ar[$i][3],1)
    Next
    _ArrayDisplay($ar)

    _GDIPlus_Shutdown()
EndFunc

Unfortunately this example (based on one now in the Help) does not exercise most of the item types, but I do not know of a file with EXIF metadata that is on most PCs. I have tested this code by updating and adding property items to torus.jpg and to a photo taken by a Sony camera. The code for implementing and calling _GDIPlus_ImageSetPropertyItem will be fairly simple [see below].

 

Your thoughts?

 

Edited by c.haslam
clarification; no bug for type 7
Spoiler

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

 

Link to comment
Share on other sites

And here is cGDIPlus_ImageSetPropertyItem, the inverse of cGDIPlus_ImageGetPropertyItem

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size,of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: @error = 0
;                  Failure: sets the @error flag to non-zero
;                  +   >0: DllCall failed. @extended contains GPSTATUS error code ($GPID_ERR*).
;                  +   -1: size cannot be negative for ASCII string and undefined types
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;                  +
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage,$a1PropertyItem)
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    Local $iBytes,$iqValues
    If $iLength>0 Then                  ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case 2 ;ASCII String
                $iqValues = 1
            Case 3 ;Array of UShort
                $iqValues = Int($iLength / 2)
            Case 4, 5, 9, 10 ;Array of UInt / Fraction
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength<0 Then              ; convenient when setting a few property items
        Switch $iType
            Case 2, 7 ;ASCII String, undefined
                Return SetError(-1)
            Case 3 ;Array of UShort
                $iBytes = -$iLength*2
            Case 4, 5, 9, 10 ;Array of UInt / Fraction
                $iBytes = -$iLength*4
            Case Else ;Array of Bytes
                $iqValues = 1
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem,'id',$iId)
    DllStructSetData($tPropItem,'length',$iBytes)
    DllStructSetData($tPropItem,'type',$iType)

    Local $tValues

    Switch $iType
        Case 2 ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case 3 ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case 4, 5 ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case 9, 10 ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType=2 Or $iType=7 Then    ; ASCII string or undefined
        DllStructSetData($tValues,1,$a1values[0])
    Else
        For $i = 0 To $iqValues-1
            DllStructSetData($tValues,1,$a1values[$i],$i+1)
        Next
    EndIf
    DllStructSetData($tPropItem,'pValue',DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "hwnd", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
EndFunc

While I was at it, I made it convenient for updating or adding properties: if the value(s) are numeric, if size is negative, the function determines the number of bytes.

Here is an example of both Get and Set:

Func Example()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile(RegRead((@AutoItX64 = True ? "HKLM\SOFTWARE\Wow6432Node\AutoIt v3\AutoIt" : "HKLM\SOFTWARE\AutoIt v3\AutoIt"), "InstallDir") & "\Examples\GUI\Torus.png")
    Local $a2PropertyItems = GetAllProjectItems($hImage)
    DisplayPropertyItems($a2PropertyItems)

    Local $hImageClone = _GDIPlus_ImageClone($hImage)   ; copy image without property items

    Local $a1[4]
    For $i = 0 To UBound($a2PropertyItems,1)-1      ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)
    Next

    ; update a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B,20,2,$a1value]       ; Artist
    cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)

    ; add a property item
    Local $a1value = [2000]
    Local $a1 = [0xa002,-1,4,$a1value]  ; ExifWidth
    cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)

    Local $a2PropertyItems = GetAllProjectItems($hImageClone)
    DisplayPropertyItems($a2PropertyItems)

    _GDIPlus_Shutdown()
EndFunc

Func GetAllProjectItems($hImage)
    Local $a2 = _GDIPlus_ImageGetPropertyIdList($hImage)
    Local $a1propertyIds[UBound($a2,1)-1]
    ; Extract ID numbers
    For $i = 1 To UBound($a2,1)-1
        $a1propertyIds[$i-1] = $a2[$i][0]
    Next

    Local $a2PropertyItems[UBound($a1propertyIds)][4],$a1
    For $i = 0 To UBound($a1propertyIds)-1
        $a1 = cGDIPlus_ImageGetPropertyItem($hImage,$a1propertyIds[$i])
        For $j = 0 To 3
            $a2PropertyItems[$i][$j] =  $a1[$j]
        Next
    Next
    Return $a2PropertyItems
EndFunc

Func DisplayPropertyItems($a2PropertyItems)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems
    For $i = 0 To UBound($a2,1)-1
        $a2[$i][0] = '0x'&Hex($a2PropertyItems[$i][0],4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3])-1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j]&'|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3],1)
    Next
    _ArrayDisplay($a2)
EndFunc

I have also tested these functions with a .jpg produced by a camera.

Edited by c.haslam
Fixed capitalization of parameter for clarity
Spoiler

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

 

Link to comment
Share on other sites

  • c.haslam changed the title to Rethinking _GDIPlus_Image*PropertyItem* functions

Here is cGDIPlus_ImageGetAllPropertyItems. Some of the code is the same as for cGDIPlus_ImageGetPropertyItem so I have rewritten cGDIPlus_ImageGetPropertyItem and added an internal function __ConvertProjectItemToNestedArrays. They are also here:

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "hwnd", $hImage, "uint*", 0, "uint*", 0)
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]
    If @error Then Return SetError(@error, @extended, -1)

    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "hwnd", $hImage, _
        "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties-1
        $a1 = __ConvertProjectItemToNestedArrays($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next
    Return $aPropertyItems
EndFunc

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
    Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(@error, @extended, -1)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
        $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)
    If @error Then Return SetError(@error, @extended, -1)

    Return __ConvertProjectItemToNestedArrays($pBuffer)
EndFunc

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __ConvertProjectItemToNestedArrays($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __ConvertProjectItemToNestedArrays($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __ConvertProjectItemToNestedArrays($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem,'type')
    Local $tValues, $iValues
    Switch $type
        Case 2 ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case 3 ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case 4, 5 ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case 9, 10 ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem,'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type=2 Or $type=7 Then  ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues,1)
    Else
        For $i = 0 To $iValues-1
            $a1values[$i] = DllStructGetData($tValues,1,$i+1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc

The example, which exemplifies Get, Set and GetAll, is here:

Func Example()
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile(RegRead((@AutoItX64 = True ? "HKLM\SOFTWARE\Wow6432Node\AutoIt v3\AutoIt" : "HKLM\SOFTWARE\AutoIt v3\AutoIt"), "InstallDir") & "\Examples\GUI\Torus.png")
    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImage)
    DisplayPropertyItems($a2PropertyItems)

    Local $hImageClone = _GDIPlus_ImageClone($hImage)   ; copy image without property items

    Local $a1[4]
    For $i = 0 To UBound($a2PropertyItems,1)-1      ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)
    Next

    ; update a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B,20,2,$a1value]       ; Artist
    cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)

    ; add a property item
    Local $a1value = [2000]
    Local $a1 = [0xa002,-1,4,$a1value]  ; ExifWidth
    cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)

    Local $a1value = [0xFFFE01]
    Local $a1 = [0x927C,3,7,$a1value]   ; MakerNote
    cGDIPlus_ImageSetPropertyItem($hImageClone,$a1)

    Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageClone)
    DisplayPropertyItems($a2PropertyItems)

    Local $a1 = cGDIPlus_ImageGetPropertyItem($hImageClone,0x927c)
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2)

    _GDIPlus_Shutdown()
EndFunc

Func DisplayPropertyItems($a2PropertyItems)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems
    For $i = 0 To UBound($a2,1)-1
        $a2[$i][0] = '0x'&Hex($a2PropertyItems[$i][0],4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3])-1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j]&'|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3],1)
    Next
    _ArrayDisplay($a2)
EndFunc

 

Edited by c.haslam
Spoiler

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

 

Link to comment
Share on other sites

I suggest that there are 2 disadvantages with the implementation of _GDIPlus_ImageGetPropertyIdList:

  • $sPropertyTagInfo, which contains 217 numbers and names, is incomplete. There are literally hundreds of tags: see http://owl.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html for a long list. Because $sPropertyTagInfo is incomplete, users of this function are likely to ask that it be updated from time to time -- when gurus would prefer to be handling real technical problems, rather than answering users.
  • StringRegExp is relatively CPU-intensive. While torus.jpg has only 8 properties, the photograph .JPEGs from my 2 cameras have 66 and 52 property items respectively, causing StringRegExp to be executed once, and the For ... Next loop to be executed many times. This is OK if a user needs all the item names but will slow down his script unnecessarily if he does not. On a photo JPEG, removing returning the names reduces the CPU time by 60%

I suggest that the Help include a link such as the one above so users can themselves link tag to tag name.

Another observation: in the Help, I found Property identifier and Property ID Info to be confusing. If you decide to keep this function as it is, I suggest Number and Name.

Further, I personally have gone to making all arrays ref 0 rather than ref 1. There may be a mixture of ref 1 and ref 0 in GDIPlus.au3.

Of course, I am free to use whatever variations of functions I like in my scripts. My suggestions are for the common good.

Edited by c.haslam
Spoiler

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

 

Link to comment
Share on other sites

@c.haslam thanks for your contribution. I need to check first in detail your posted code and do some research. This may take some days (weeks?).

 

@Melba23, @JLogan3o13 can you move this topic to "AutoIt Technical Discussion" please?

 

 

Edited by UEZ

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

In debugging my versions of the above functions, I have used one function and one tool.

The function is here:

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ReportIfErrorInDLLcall
; Description ...: Reports and exits if @error non-zero or $aResult[0] non-zero
; Syntax ........: cGDIPlus_ReportIfErrorInDLLcall($iError, $iStatus, $sFuncName)
; Parameters ....: $iError             - @error from DllCall()
;                  $iStatus            - $aResult[0] from DLLCall
;                  $sFuncName          - function parameter of DLLCall
; Return values .: None
; Author ........: c.haslam
; Modified ......:
; Remarks .......: Example: cGDIPlus_ReportIfErrorInDLLCall(@error, $aResult[0],'GdipSetPropItemItem')
; Related .......: all GDIPlus_* functions
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms534175(v=vs.85).aspx
; Example .......: No
; ===============================================================================================================================

Func cGDIPlus_ReportIfErrorInDLLcall($iError,$iStatus,$sFuncName)
    If $iError=0 And $iStatus=0 Then
        Return
    EndIf
    If $iError<>0 Then
        MsgBox($MB_ICONERROR,'GDIPlus call error',$sFuncName&' @error = '&$iError)
        Exit
    EndIf

Local $a1Status = ['Ok','GenericError','InvalidParameter','OutOfMemory','ObjectBusy','InsufficientBuffer', _
    'NotImplemented','Win32Error','WrongState','Aborted','FileNotFound','ValueOverflow','AccessDenied', _
    'UnknownImageFormat','FontFamilyNotFound','FontStyleNotFound','NotTrueTypeFont','UnsupportedGdiplusVersion', _
    'GdiplusNotInitialized','PropertyNotFound','PropertyNotSupported','ProfileNotFound']
    If $iStatus<>0 Then
        MsgBox($MB_ICONERROR,'GDIPlus call error',$sFuncName&' '&$a1Status[$iStatus])
        Exit
    EndIf
EndFunc

Because users should only be calling GDIPlus_* functions, they should not be calling DLLCall(), so they should not need this function (unless they forget to call GDIPlus_Startup!); however, there are some 600 GDIPlus_* functions, so some may write functions that are not yet in GDIPlus.au3. For them, this function may be useful. So perhaps it should be added to GDIPlus.au3.

The tool is my cDebug. I have used it a lot to report structures and nested arrays.

Spoiler

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

 

Link to comment
Share on other sites

UEZ,

It just occurred to me to mention that types 6 (signed byte) and 8 (signed short) were in the original EXIF standard, but, as far as I can figure out, were never used and have been removed from the current standard.

For the list of types per M$, see https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx

Spoiler

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

 

Link to comment
Share on other sites

UEZ,

A word of warning: when I incorporate calls to my cGDI* functions into my DeskewaPic function (which is based on the one you provided months back), AutoIt crashes. I am yet to find out why, despite considerable effort. I have never seen examples for my cGDI* functions crash AutoIt. My script does not crash when cGDI* functions are not called.

I do remember that you posted making (I think) the buffer static. As I have time, I will put some more effort in that direction.

For my DeskewaPic function, I need to copy all property items from the skewed picture to the de-skewed picture, and update 3 property items: DateTIme (ox132), ExifImageWidth (0xA002) and ExifImageHeight (0xa003).

Edited by c.haslam
Spoiler

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

 

Link to comment
Share on other sites

  • 2 weeks later...

@c.haslam I checked out shortly what you did I like your approach.

Let me dig in a little bit more...

 

Thanks so far for your contribution to GDI+. :thumbsup:

Edited by UEZ

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

What I did so far:

#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>

;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, _
             $GDIP_PROPERTYTAGTYPELONG = 4, $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, _
             $GDIP_PROPERTYTAGTYPESLONG = 9, $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

Global $aPropID

Example()


Func Example()
    Local $sImagefile = FileOpenDialog("Select a JPG image file", "", "JPG Image (*.jpg;*.jpeg)")
    If @error Then Exit
    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile($sImagefile)
    $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
    Local $a2PropertyItems = _GDIPlus_ImageGetAllPropertyItems($hImage)
    DisplayPropertyItems($a2PropertyItems)

    Local $hImageClone = _GDIPlus_ImageClone($hImage) ; copy image without property items

    Local $a1[4]
    For $i = 0 To UBound($a2PropertyItems, 1) - 1 ; set all property items
        For $j = 0 To 3
            $a1[$j] = $a2PropertyItems[$i][$j]
        Next
        _GDIPlus_ImageSetPropertyItem($hImageClone, $a1)
    Next

    ; update a property item
    Local $a1value = ['Your name']
    Local $a1 = [0x13B, 20, 2, $a1value] ; Artist
    _GDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    ; add a property item
    Local $a1value = [2000]
    Local $a1 = [0xa002, -1, 4, $a1value] ; ExifWidth
    _GDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    Local $a1value = [0xFFFE01]
    Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote
    _GDIPlus_ImageSetPropertyItem($hImageClone, $a1)

    $a2PropertyItems = _GDIPlus_ImageGetAllPropertyItems($hImageClone)
    DisplayPropertyItems($a2PropertyItems)

    $a1 = _GDIPlus_ImageGetPropertyItemEx($hImageClone, 0x927c) ;https://msdn.microsoft.com/en-us/library/ms534417.aspx
    Local $a2[1][4]
    For $i = 0 To 3
        $a2[0][$i] = $a1[$i]
    Next
    DisplayPropertyItems($a2)

    _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func DisplayPropertyItems($a2PropertyItems)
    ; Collapse values arrays so _ArrayDisplay can display them
    Local $a2 = $a2PropertyItems, $iPos
    ReDim $a2[UBound($a2PropertyItems)][5]

    For $i = 0 To UBound($a2, 1) - 1
        $a2[$i][0] = '0x' & Hex($a2PropertyItems[$i][0], 4)
        $a2[$i][3] = ''
        For $j = 0 To UBound($a2PropertyItems[$i][3]) - 1
            $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j] & '|'
        Next
        $a2[$i][3] = StringTrimRight($a2[$i][3], 1)
        $iPos = _ArraySearch($aPropID, $a2[$i][0])
        If $iPos > -1 Then $a2[$i][4] = $aPropID[$iPos][1]
    Next
    _ArrayDisplay($a2, "Image Property Items", Default, 0, Default, "Identifier|Size|Type|Value|Description")
EndFunc   ;==>DisplayPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: _GDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyItemEx, _GDIPlus_ImageGetPropertyIdList, _GDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func _GDIPlus_ImageGetAllPropertyItems($hImage)
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
    If @error Then Return SetError(@error + 10, @extended, -1)
    If $aResult[0] Then Return SetError(20, $aResult[0], -1)
    
    Local $iTotalBufferSize = $aResult[2]
    Local $iNumProperties = $aResult[3]
    Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
    Local $pBuffer = DllStructGetPtr($tBuffer)

    $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
            "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)

    Local $aPropertyItems[$iNumProperties][4]
    Local $a1
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

    For $iI = 0 To $iNumProperties - 1
        $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
        For $j = 0 To 3
            $aPropertyItems[$iI][$j] = $a1[$j]
        Next
        $pBuffer += DllStructGetSize($tPropertyItem)
    Next

    Return $aPropertyItems
EndFunc   ;==>_GDIPlus_ImageGetAllPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_ImageGetPropertyItemEx
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: _GDIPlus_ImageGetPropertyItemEx($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, _GDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func _GDIPlus_ImageGetPropertyItemEx($hImage, $iPropID)
    Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
    If @error Then Return SetError(@error + 10, @extended, False)

    Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
    Local $pBuffer = DllStructGetPtr($tBuffer)
    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
            $iPropID, "uint", $iSize, "struct*", $tBuffer)
    If @error Then Return SetError(@error, @extended, False)
    If $aResult[0] Then Return SetError(10, $aResult[0], False)

    Return __GDIPlus_ImageGetPropertyItemValues($pBuffer)
EndFunc   ;==>_GDIPlus_ImageGetPropertyItemEx

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================
Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
    Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
    Local $iBytes = DllStructGetData($tPropertyItem, "length")
    Local $pValue = DllStructGetData($tPropertyItem, "value")

    Local $aRet[4]
    Local $type = DllStructGetData($tPropertyItem, 'type')
    Local $tValues, $iValues

    Switch $type
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $iValues = 1
            $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $iValues = Int($iBytes / 2)
            $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $iValues = Int($iBytes / 4)
            $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
        Case Else ;Array of Bytes
            $iValues = 1
            $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
    EndSwitch
    $aRet[0] = DllStructGetData($tPropertyItem, 'id')
    $aRet[1] = $iBytes
    $aRet[2] = $type
    Local $a1values[$iValues]
    If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        $a1values[0] = DllStructGetData($tValues, 1)
    Else
        For $i = 0 To $iValues - 1
            $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
        Next
    EndIf
    $aRet[3] = $a1values
    Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: _GDIPlus_ImageGetPropertyItemEx($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size,of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to non-zero
;                  +   >0: DllCall failed. @extended contains GPSTATUS error code ($GPID_ERR*).
;                  +   -1: size cannot be negative for ASCII string and undefined types
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;                  +
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, _GDIPlus_ImageGetPropertyItemEx
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func _GDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
    Local $iId = $a1PropertyItem[0]
    Local $iLength = $a1PropertyItem[1]
    Local $iType = $a1PropertyItem[2]
    Local $a1values = $a1PropertyItem[3]

    Local $iBytes, $iqValues
    If $iLength > 0 Then ; useful when copying all property items
        $iBytes = $iLength
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
                $iqValues = 1
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iqValues = Int($iLength / 2)
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iqValues = Int($iLength / 4)
            Case Else ; Array of Bytes, undefined
                $iqValues = 1
        EndSwitch
    ElseIf $iLength < 0 Then ; convenient when setting a few property items
        Switch $iType
            Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
                Return SetError(-1)
            Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
                $iBytes = -$iLength * 2
            Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
                $iBytes = -$iLength * 4
            Case Else ;Array of Bytes
                $iqValues = 1
                $iBytes = -$iLength
        EndSwitch
        $iqValues = -$iLength
    EndIf

    Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
    DllStructSetData($tPropItem, 'id', $iId)
    DllStructSetData($tPropItem, 'length', $iBytes)
    DllStructSetData($tPropItem, 'type', $iType)

    Local $tValues

    Switch $iType
        Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
            $tValues = DllStructCreate("char[" & $iBytes & "];")
        Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
            $tValues = DllStructCreate("ushort[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
            $tValues = DllStructCreate("uint[" & $iqValues & "];")
        Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
            $tValues = DllStructCreate("int[" & $iqValues & "];")
        Case Else ;Array of Bytes
            $tValues = DllStructCreate("byte[" & $iBytes & "];")
    EndSwitch
    If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
        DllStructSetData($tValues, 1, $a1values[0])
    Else
        For $i = 0 To $iqValues - 1
            DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
        Next
    EndIf
    DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
    If @error Then Return SetError(@error, @extended, -1)
    If $aResult[0] Then Return SetError(10, $aResult[0], -1)
    Return True
EndFunc   ;==>_GDIPlus_ImageSetPropertyItem

 

Any suggestion?

Edited by UEZ
added JPM's suggestions

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

error checking is not perfect

can those functions replace the current UDF standard without script breaking

#AutoIt3Wrapper_UseX64=n
#include <GDIPlus.au3>
#include <Array.au3>
#include <WinAPIDiag.au3>


;--------- should be moved to GDIPlusConstants.au3
Global Const $GDIP_PROPERTYTAGTYPEBYTE = 1, $GDIP_PROPERTYTAGTYPEASCII = 2, $GDIP_PROPERTYTAGTYPESHORT = 3, $GDIP_PROPERTYTAGTYPELONG = 4, _
  $GDIP_PROPERTYTAGTYPERATIONAL = 5, $GDIP_PROPERTYTAGTYPEUNDEFINED = 7, $GDIP_PROPERTYTAGTYPESLONG = 9, _
  $GDIP_PROPERTYTAGTYPESRATIONAL = 10
;-------------------------------------------------

Global $aPropID

Example()


Func Example()
 Local $sImagefile = FileOpenDialog("Select a JPG image file", "", "JPG Image (*.jpg;*.jpeg)")
 If @error Then Exit
 _GDIPlus_Startup()
 Local $hImage = _GDIPlus_ImageLoadFromFile($sImagefile)
 $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage)
 Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImage)
 DisplayPropertyItems($a2PropertyItems)

 Local $hImageClone = _GDIPlus_ImageClone($hImage) ; copy image without property items

 Local $a1[4]
 For $i = 0 To UBound($a2PropertyItems, 1) - 1 ; set all property items
  For $j = 0 To 3
   $a1[$j] = $a2PropertyItems[$i][$j]
  Next
  cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)
 Next

 ; update a property item
 Local $a1value = ['Your name']
 Local $a1 = [0x13B, 20, 2, $a1value] ; Artist
 cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

 ; add a property item
 Local $a1value = [2000]
 Local $a1 = [0xa002, -1, 4, $a1value] ; ExifWidth
 cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

 Local $a1value = [0xFFFE01]
 Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote
 cGDIPlus_ImageSetPropertyItem($hImageClone, $a1)

 $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageClone)
 DisplayPropertyItems($a2PropertyItems)

 $a1 = cGDIPlus_ImageGetPropertyItem($hImageClone, 0x927c) ;https://msdn.microsoft.com/en-us/library/ms534417.aspx
 Local $a2[1][4]
 For $i = 0 To 3
  $a2[0][$i] = $a1[$i]
 Next
 DisplayPropertyItems($a2)

 _GDIPlus_Shutdown()
EndFunc   ;==>Example

Func DisplayPropertyItems($a2PropertyItems)
 ; Collapse values arrays so _ArrayDisplay can display them
 Local $a2 = $a2PropertyItems, $iPos
 ReDim $a2[UBound($a2PropertyItems)][5]

 For $i = 0 To UBound($a2, 1) - 1
  $a2[$i][0] = '0x' & Hex($a2PropertyItems[$i][0], 4)
  $a2[$i][3] = ''
  For $j = 0 To UBound($a2PropertyItems[$i][3]) - 1
   $a2[$i][3] &= ($a2PropertyItems[$i][3])[$j] & '|'
  Next
  $a2[$i][3] = StringTrimRight($a2[$i][3], 1)
  $iPos = _ArraySearch($aPropID, $a2[$i][0])
  If $iPos > -1 Then $a2[$i][4] = $aPropID[$iPos][1]
 Next
 _ArrayDisplay($a2, "Image Property Items", Default, 0, Default, "Identifier|Size|Type|Value|Description")
EndFunc   ;==>DisplayPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetAllPropertyItems
; Description ...: Gets all property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetAllPropertyItems($hImage)
; Parameters ....: $hImage              - Pointer to an image object
; Return values .: Success: Array in which each row contains these columns:
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: c.haslam
; Modified ......:
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: cGDIPlus_ImageGetPropertyItem, _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================

Func cGDIPlus_ImageGetAllPropertyItems($hImage)
 Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetPropertySize", "handle", $hImage, "uint*", 0, "uint*", 0)
 Local $iTotalBufferSize = $aResult[2]
 Local $iNumProperties = $aResult[3]
 If @error Then Return SetError(@error + 10, @extended, -1)
 If $aResult[0] Then Return SetError(20, $aResult[0], -1)

 Local $tBuffer = DllStructCreate("byte[" & $iTotalBufferSize & "]")
 Local $pBuffer = DllStructGetPtr($tBuffer)

 $aResult = DllCall($__g_hGDIPDll, "uint", "GdipGetAllPropertyItems", "handle", $hImage, _
   "uint", $iTotalBufferSize, "uint", $iNumProperties, "ptr", $pBuffer)
 If @error Then Return SetError(@error, @extended, -1)
 If $aResult[0] Then Return SetError(10, $aResult[0], -1)

 Local $aPropertyItems[$iNumProperties][4]
 Local $a1
 Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;")

 For $iI = 0 To $iNumProperties - 1
  $a1 = __GDIPlus_ImageGetPropertyItemValues($pBuffer)
  For $j = 0 To 3
   $aPropertyItems[$iI][$j] = $a1[$j]
  Next
  $pBuffer += DllStructGetSize($tPropertyItem)
 Next

 Return $aPropertyItems
EndFunc   ;==>cGDIPlus_ImageGetAllPropertyItems

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageGetPropertyItem
; Description ...: Gets a specified property item (piece of meta data) from an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
; Parameters ....: $hImage              - Pointer to an image object
;                  $iPropID             - Identifier of the property item to be retrieved
; Return values .: Success: Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
;                  Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).
; Author ........: Eukalyptus
; Modified ......: c.haslam
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageSetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/ms534416.aspx#_gdiplus_constant_propertytaggpsver
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageGetPropertyItem($hImage, $iPropID)
 Local $iSize = __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID)
 If @error Then Return SetError(@error + 10, @extended, False)

 Local $tBuffer = DllStructCreate("byte[" & $iSize & "];")
 Local $pBuffer = DllStructGetPtr($tBuffer)
 Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItem", "handle", $hImage, "uint", _
   $iPropID, "uint", $iSize, "struct*", $tBuffer)
 If @error Then Return SetError(@error, @extended, False)
 If $aResult[0] Then Return SetError(10, $aResult[0], False)

 Return __GDIPlus_ImageGetPropertyItemValues($pBuffer)
EndFunc   ;==>cGDIPlus_ImageGetPropertyItem

; #INTERNAL_USE_ONLY# ===========================================================================================================
; Name ..........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Description ...: Converts a project item from structured to equivalent nested arrays
; Syntax ........: __GDIPlus_ImageGetPropertyItemValues($pBuffer)
; Parameters ....: $bBuffer             - Pointer to start of Property Item sructure
; Return values .:     [0] - identifier
;                      [1] - size, in bytes, of the value array
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Author ........: c.haslam
; Modified ......: UEZ
; Remarks .......: types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsigned rational = 5, signed byte = 6, undefined = 7, signed long = 9,
;                         signed rational = 10
; Related .......:
; Link ..........:
; Example .......: No
; ===============================================================================================================================

Func __GDIPlus_ImageGetPropertyItemValues($pBuffer)
 Local $tPropertyItem = DllStructCreate("int id; int length; short type; ptr value;", $pBuffer)
 Local $iBytes = DllStructGetData($tPropertyItem, "length")
 Local $pValue = DllStructGetData($tPropertyItem, "value")

 Local $aRet[4]
 Local $type = DllStructGetData($tPropertyItem, 'type')
 Local $tValues, $iValues

 Switch $type
  Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
   $iValues = 1
   $tValues = DllStructCreate("char[" & $iBytes & "];", $pValue)
  Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
   $iValues = Int($iBytes / 2)
   $tValues = DllStructCreate("ushort[" & $iValues & "];", $pValue)
  Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
   $iValues = Int($iBytes / 4)
   $tValues = DllStructCreate("uint[" & $iValues & "];", $pValue)
  Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
   $iValues = Int($iBytes / 4)
   $tValues = DllStructCreate("int[" & $iValues & "];", $pValue)
  Case Else ;Array of Bytes
   $iValues = 1
   $tValues = DllStructCreate("byte[" & $iBytes & "];", $pValue)
 EndSwitch
 $aRet[0] = DllStructGetData($tPropertyItem, 'id')
 $aRet[1] = $iBytes
 $aRet[2] = $type
 Local $a1values[$iValues]
 If $type = $GDIP_PROPERTYTAGTYPEASCII Or $type = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
  $a1values[0] = DllStructGetData($tValues, 1)
 Else
  For $i = 0 To $iValues - 1
   $a1values[$i] = DllStructGetData($tValues, 1, $i + 1)
  Next
 EndIf
 $aRet[3] = $a1values
 Return $aRet
EndFunc   ;==>__GDIPlus_ImageGetPropertyItemValues

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ImageSetPropertyItem
; Description ...: Sets a specified property item (piece of meta data) for an Image object
; Syntax ........: cGDIPlus_ImageGetPropertyItem($hImage, $a1PropertyItem)
; Parameters ....: $hImage              - Pointer to an image object
;                  $a1PropertyItem      - Array containing the values of the property item
;                      [0] - identifier
;                      [1] - size,of the value array
;                               positive: in bytes
;                               negative: for numeric types: in number of values
;                      [2] - type of value(s) in the value array
;                      [3] - value array
; Return values .: Success: True and @error = 0
;                  Failure: sets the @error flag to non-zero
;                  +   >0: DllCall failed. @extended contains GPSTATUS error code ($GPID_ERR*).
;                  +   -1: size cannot be negative for ASCII string and undefined types
; Author ........: Eukalyptus
; Modified ......: c.haslam, UEZ
; Remarks .......: If size parameter is negative, calculates size in bytes from type.
;                  Convenient when setting a few property items
;                  +
;                  types: unsigned byte = 1, ASCII string = 2, unsigned short = 3, unsigned long = 4,
;                         unsinged rational = 5, undefined = 7, signed long = 9, signed rational = 10
; Related .......: _GDIPlus_ImageGetPropertyIdList, cGDIPlus_ImageGetPropertyItem
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms535390(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534493(v=vs.85).aspx,
;                  https://msdn.microsoft.com/en-us/library/windows/desktop/ms534414(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================
Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem)
 Local $iId = $a1PropertyItem[0]
 Local $iLength = $a1PropertyItem[1]
 Local $iType = $a1PropertyItem[2]
 Local $a1values = $a1PropertyItem[3]

 Local $iBytes, $iqValues
 If $iLength > 0 Then ; useful when copying all property items
  $iBytes = $iLength
  Switch $iType
   Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
    $iqValues = 1
   Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
    $iqValues = Int($iLength / 2)
   Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
    $iqValues = Int($iLength / 4)
   Case Else ; Array of Bytes, undefined
    $iqValues = 1
  EndSwitch
 ElseIf $iLength < 0 Then ; convenient when setting a few property items
  Switch $iType
   Case $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPEUNDEFINED ;ASCII String, undefined
    Return SetError(-1)
   Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
    $iBytes = -$iLength * 2
   Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction
    $iBytes = -$iLength * 4
   Case Else ;Array of Bytes
    $iqValues = 1
    $iBytes = -$iLength
  EndSwitch
  $iqValues = -$iLength
 EndIf

 Local $tPropItem = DllStructCreate("int id; int length; short type; ptr pValue;")
 DllStructSetData($tPropItem, 'id', $iId)
 DllStructSetData($tPropItem, 'length', $iBytes)
 DllStructSetData($tPropItem, 'type', $iType)

 Local $tValues

 Switch $iType
  Case $GDIP_PROPERTYTAGTYPEASCII ;ASCII String
   $tValues = DllStructCreate("char[" & $iBytes & "];")
  Case $GDIP_PROPERTYTAGTYPESHORT ;Array of UShort
   $tValues = DllStructCreate("ushort[" & $iqValues & "];")
  Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL ;Array of UInt / Fraction
   $tValues = DllStructCreate("uint[" & $iqValues & "];")
  Case $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of Int / Fraction
   $tValues = DllStructCreate("int[" & $iqValues & "];")
  Case Else ;Array of Bytes
   $tValues = DllStructCreate("byte[" & $iBytes & "];")
 EndSwitch
 If $iType = $GDIP_PROPERTYTAGTYPEASCII Or $iType = $GDIP_PROPERTYTAGTYPEUNDEFINED Then ; ASCII string or undefined
  DllStructSetData($tValues, 1, $a1values[0])
 Else
  For $i = 0 To $iqValues - 1
   DllStructSetData($tValues, 1, $a1values[$i], $i + 1)
  Next
 EndIf
 DllStructSetData($tPropItem, 'pValue', DllStructGetPtr($tValues))

 Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetPropertyItem", "handle", $hImage, "struct*", $tPropItem)
 If @error Then Return SetError(@error, @extended, -1)
 If $aResult[0] Then Return SetError(10, $aResult[0], -1)
 Return True
EndFunc   ;==>cGDIPlus_ImageSetPropertyItem

 

Edited by Melba23
Code tags and not quote
Link to comment
Share on other sites

For many _GDI* functions as they are now (including mine and my revisions of functions written by others), the Help says

Failure: Sets the @error flag to non-zero, @extended may contain GPSTATUS error code ($GPID_ERR*).

This does not say where to find the $GPID_ERR* constants.

IMHO it should be possible to use any of the _GDI functions without needing to look at GDIPlus.au3 or GDIPlusConstants.au3: the Help should be sufficient.

So I suggest that a function be added for which the Help might be:

; #FUNCTION# ====================================================================================================================
; Name ..........: cGDIPlus_ReportIfErroIFromCall
; Description ...: Checks for errors in calling any other GDIPlus function
; Syntax ........: cGDIPlus_ReportIfErroFromCall($iError, $iStatus, $sFuncName)
; Parameters ....: $iError             - @error returned by a _GDIPlus* function
;                  $iExtended          - @extended returned by a _GDIPlus* function
;                  $sFuncName          - name of _GDIPlus* function
; Return values .: None
; Author ........: c.haslam
; Modified ......:
; Renmarks ......: Useful for diagnosing errors in calling _GDIPlus functions
;                  If _GDIPlus* function set neither @error nor @extended, this function does nothing, so can be left ion your debugged script
; Related .......: all other _GDIPlus_* functions
; Link ..........: https://msdn.microsoft.com/en-us/library/windows/desktop/ms534175(v=vs.85).aspx
; Example .......: Yes
; ===============================================================================================================================

The Help for many other functions might then say something like:

Failure: Sets @error flag to non-zero. @extended may contain additional information. Call cGDIPlus_ReportIFErrorFrom Call() for details.

I have a draft of cGDIPlus_ReportIFErroFromCall(), but I need to look at the error reporting in all the GDIPlus* functions before suggesting what the code should be. It would avoid the user having to look in GDIPlusConstants.au3 for the error code.

I do now recognize that the Help for cGDIPlus_ImageGetAllProperties() does not mention the error code returned when the user forgets to call GDIPLus_Startup(). Indeed, I need to do some more thinking about what to set @extended to (and may be @error) when there are 2 DLLCalls in a GDIPlus* function, as is the case with cGDIPlus_ImageGetAllProperties(). It needs to be consistent with already existing GDIPlus* functions.

 

Edited by c.haslam
Spoiler

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

 

Link to comment
Share on other sites

why not to mention ($GPID_ERR* defined in GDIPLusConstants.au3) certainly other GPIPlus UDF must be updated too.

in other standard UDF when the error code is completly defined the related *Constants.au3 is only mention when it is not included by the .au3 which define the function

Link to comment
Share on other sites

True: other standard UDFs do only mention the related *Constants.au3 when it is not included by the .au3; however, the user can be helped further by including a function such as I am suggesting. In working on an application script I have found an error-reporting function to save a chunk of development time.

It is up to the developers to decide whether GDIPlus,au3 goes beyond what has been done for other standard UDFs.

 

Spoiler

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

 

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

×
×
  • Create New...