jpm Posted May 14, 2018 Posted May 14, 2018 I update the ($GPIP_ERR*) in ($GPIP_ERR* see GDIPlusConstants.au3) in all current functions Cheers
c.haslam Posted May 14, 2018 Author Posted May 14, 2018 Will you change my GDIPlus* functions to set @error and @extended the same as other GDIPlus* functions, or do you wish me to change my functions? As cGDIPlus_ImageSetPropertyItem is now, it does not detect a number of user errors. Given Func cGDIPlus_ImageSetPropertyItem($hImage, $a1PropertyItem) Local $iId = $a1PropertyItem[0] Local $iLength = $a1PropertyItem[1] Local $iType = $a1PropertyItem[2] Local $a1values = $a1PropertyItem[3] Some of the errors it does not detect are: $iType is not one of the $GDIP_PROPERTYTAGTYPE* constants $a1Values is not a 1-d array (a crasher in my function) If $iType not ASCII, $iLength and UBound($a1values) don't agree If $iType is $GDIP_PROPERTYTAGTYPELONG, $a1values[0] not a multiple of 4 $iId is not an Int UBound($a1PropertyItem) not 4 ( a crasher in my function) All of the non-crashers above are not caught by my function. It has 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) In these situations (and perhaps others) should my function set @error (and @extended)? If so, to what values? Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
c.haslam Posted May 15, 2018 Author Posted May 15, 2018 (edited) Here are my functions with error checking: expandcollapse popup#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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $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 ;==>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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 2: $a1PropertyItem is not a 4-element 1-d array ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 101: identifier is not an integer ; 201: size is not an integer ; 202: size cannot be number of values for ASCII string and undefined types ; 202: size is zero ; 301: type is illegal ; 401: value array is either not a 1-d array or has no elements ; 402: size is incompatible with type ; 403: size cannot be negative for ASCII string and undefined types ; 404: Numerator and denominator are required for rational types ; 405: size and number of elements in value array differ ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then Return SetError(2, 0, False) EndIf Local $iId = $a1PropertyItem[0] Local $iLength = $a1PropertyItem[1] Local $iType = $a1PropertyItem[2] Local $a1values = $a1PropertyItem[3] If Not IsInt($iId) Then Return SetError(101, 0, False) EndIf If Not IsInt($iLength) Then Return SetError(201, 0, False) EndIf If $iLength = 0 Then Return SetError(202, 0, False) EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _ $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ; do nothing Case Else Return SetError(301, 0, False) EndSwitch If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then Return SetError(401, 0, False) EndIf 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 If BitAND($iLength, 1) <> 0 Then Return SetError(402, 0, False) EndIf $iqValues = Int($iLength / 2) Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _ $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction If BitAND($iLength, 3) <> 0 Then Return SetError(402, 0, False) EndIf $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(403, 0, False) 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 $iBytes = -$iLength EndSwitch $iqValues = -$iLength EndIf If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then If BitAND($iqValues, 1) <> 0 Then Return SetError(404, 0, False) EndIf EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL If UBound($a1values)<>$iqValues Then Return SetError(405,0,False) EndIf EndSwitch 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 I have been unable to get the DLLCall to fail for any other reason than $__g_hGDIPDll not being set by _GDIPlus_Startup(). When DLLCall fails, the error code always, in my experience, is 1. Looking at the Help for DLLCall, this makes sense to me. I am willing to change the error codes if you wish. Edited May 16, 2018 by c.haslam typo Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
c.haslam Posted May 18, 2018 Author Posted May 18, 2018 (edited) I have discovered that, if a property item does not exist in a JPG, GdipGetPropertyItemSize does not set a $GDIP_ERR*. The listing below includes c__GDIPlus_ImageGetPropertyItemSize(), which handles this situation. I have also changed the example: adding a case where a property item does not exist in the JPG file using a JPG file that is included in AutoIt distribution, rather than a JPG file the user chooses, so the property item tested in point 1 will always not occur I wonder if there are other Gdip* functions for which no $GDIP_ERR code is set. expandcollapse popup#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 $a2PropID Example() Func Example() _GDIPlus_Startup() Local $hImage = _GDIPlus_ImageLoadFromFile('F:\Program Files\AutoIt3\Examples\GUI\mslogo.jpg') $a2PropID = _GDIPlus_ImageGetPropertyIdList($hImage) Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImage) DisplayPropertyItems($a2PropertyItems) Local $a1 = cGDIPlus_ImageGetPropertyItem($hImage,0x138) If @error=10 Then If @extended=$GDIP_ERRPROPERTYNOTFOUND Then MsgBox(0,'','Property item 0x138 not found in JPG file') Else MsgBox(0,@ScriptLineNumber,'GDIPlus error '&@extended) EndIf Else Local $a2[1][4] For $i = 0 To 3 $a2[0][$i] = $a1[$i] Next DisplayPropertyItems($a2) EndIf 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($a2PropID, $a2[$i][0]) If $iPos > -1 Then $a2[$i][4] = $a2PropID[$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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $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 ;==>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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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 = c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) If @error Then Return SetError(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 ..........: c__GDIPlus_ImageGetPropertyItemSize ; Description ...: Gets the size, in bytes, of a specified property item of an image object ; Syntax ........: __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) ; Parameters ....: $hImage - Pointer to an image object ; $iPropID - Identifier of the property item to be retrieved ; Return values .: Success - Size in bytes of the property item ; Failure: sets the @error flag to: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found ; Author ........: Authenticity ; Modified ......: Eukalyptus, c.haslam ; Remarks .......: ; Related .......: _GDIPlus_ImageGetPropertyItem ; Link ..........: @@MsdnLink@@ GdipGetPropertyItemSize ; Example .......: No ; =============================================================================================================================== Func c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItemSize", "handle", $hImage, "uint", $iPropID, "uint*", 0) If $aResult[3]=-1 Then Return SetError(10,$GDIP_ERRPROPERTYNOTFOUND,-1) ; GdipGetPropertyItemSize doesn't set it! EndIf If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $aResult[0], -1) Return $aResult[3] EndFunc ;==>__GDIPlus_ImageGetPropertyItemSize ; #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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 2: $a1PropertyItem is not a 4-element 1-d array ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 101: identifier is not an integer ; 201: size is not an integer ; 202: size cannot be number of values for ASCII string and undefined types ; 202: size is zero ; 301: type is illegal ; 401: value array is either not a 1-d array or has no elements ; 402: size is incompatible with type ; 403: size cannot be negative for ASCII string and undefined types ; 404: Numerator and denominator are required for rational types ; 405: size and number of elements in value array differ ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then Return SetError(2, 0, False) EndIf Local $iId = $a1PropertyItem[0] Local $iLength = $a1PropertyItem[1] Local $iType = $a1PropertyItem[2] Local $a1values = $a1PropertyItem[3] If Not IsInt($iId) Then Return SetError(101, 0, False) EndIf If Not IsInt($iLength) Then Return SetError(201, 0, False) EndIf If $iLength = 0 Then Return SetError(202, 0, False) EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _ $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ; do nothing Case Else Return SetError(301, 0, False) EndSwitch If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then Return SetError(401, 0, False) EndIf 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 If BitAND($iLength, 1) <> 0 Then Return SetError(402, 0, False) EndIf $iqValues = Int($iLength / 2) Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _ $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction If BitAND($iLength, 3) <> 0 Then Return SetError(402, 0, False) EndIf $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(403, 0, False) 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 $iBytes = -$iLength EndSwitch $iqValues = -$iLength EndIf If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then If BitAND($iqValues, 1) <> 0 Then Return SetError(404, 0, False) EndIf EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL If UBound($a1values)<>$iqValues Then Return SetError(405,0,False) EndIf EndSwitch 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 May 19, 2018 by c.haslam Clarified help for cGDIPlus_ImageGetPropertyItem and c__GDIPlus_ImageGetPropertyItemSize Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
c.haslam Posted May 19, 2018 Author Posted May 19, 2018 (edited) I have learnt that a cloned image has the same properties as the original image. I then discovered how to create a copy of an image that has property items to an image that does not. This caused changes in Example(). expandcollapse popup#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 $a2PropID Example() Func Example() _GDIPlus_Startup() Local $hImageF = _GDIPlus_ImageLoadFromFile('F:\Program Files\AutoIt3\Examples\GUI\mslogo.jpg') $a2PropID = _GDIPlus_ImageGetPropertyIdList($hImageF) Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF) DisplayPropertyItems($a2PropertyItems,'as read') Local $a1 = cGDIPlus_ImageGetPropertyItem($hImageF,0x138) If @error=10 Then If @extended=$GDIP_ERRPROPERTYNOTFOUND Then MsgBox(0,'','Property item 0x138 not found in image from JPG file') Else MsgBox(0,@ScriptLineNumber,'GDIPlus error '&@extended) EndIf EndIf Local $nWidImg = _GDIPlus_ImageGetWidth($hImageF) Local $nHtImg = _GDIPlus_ImageGetHeight($hImageF) ; hImageN has no property items Local $hImageN = _GDIPlus_BitmapCloneArea($hImageF,$nWidImg,$nHtImg,$nWidImg,$nHtImg) Local $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageN) If $a2PropertyItems=-1 Then MsgBox(0,'','No property items') EndIf 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($hImageN, $a1) Next ; add a property item Local $a1value = ['Your name'] Local $a1 = [0x13B, 20, 2, $a1value] ; Artist cGDIPlus_ImageSetPropertyItem($hImageF, $a1) ; add a property item Local $a1value = [2] Local $a1 = [0xa002, -1, 3, $a1value] ; ResolutionUnit cGDIPlus_ImageSetPropertyItem($hImageF, $a1) $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF) Local $a2[1][4] For $i = 0 To 3 $a2[0][$i] = $a1[$i] Next DisplayPropertyItems($a2PropertyItems,'from file, after adding') ; add a property item Local $a1value = [0xFFFE01] Local $a1 = [0x927C, 3, 7, $a1value] ; MakerNote cGDIPlus_ImageSetPropertyItem($hImageN, $a1) $a2PropertyItems = cGDIPlus_ImageGetAllPropertyItems($hImageF) DisplayPropertyItems($a2PropertyItems,'') $a1 = cGDIPlus_ImageGetPropertyItem($hImageF, 0x13B) ;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,'one item') _GDIPlus_ImageDispose($hImageF) _GDIPlus_ImageDispose($hImageN) _GDIPlus_Shutdown() EndFunc ;==>Example Func DisplayPropertyItems($a2PropertyItems,$str) ; 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($a2PropID, $a2[$i][0]) If $iPos > -1 Then $a2[$i][4] = $a2PropID[$iPos][1] Next _ArrayDisplay($a2, "Image Property Items " &$str, 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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; @extended=2; no property items found ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $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 ;==>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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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 = c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) If @error Then Return SetError(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 ..........: c__GDIPlus_ImageGetPropertyItemSize ; Description ...: Gets the size, in bytes, of a specified property item of an image object ; Syntax ........: __GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) ; Parameters ....: $hImage - Pointer to an image object ; $iPropID - Identifier of the property item to be retrieved ; Return values .: Success - Size in bytes of the property item ; Failure: sets the @error flag to: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; @extended=19 ($$GDIP_ERRPROPERTYNOTFOUND): property item not found ; Author ........: Authenticity ; Modified ......: Eukalyptus, c.haslam ; Remarks .......: ; Related .......: _GDIPlus_ImageGetPropertyItem ; Link ..........: @@MsdnLink@@ GdipGetPropertyItemSize ; Example .......: No ; =============================================================================================================================== Func c__GDIPlus_ImageGetPropertyItemSize($hImage, $iPropID) Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetPropertyItemSize", "handle", $hImage, "uint", $iPropID, "uint*", 0) If $aResult[3]=-1 Then Return SetError(10,$GDIP_ERRPROPERTYNOTFOUND,-1) ; GdipGetPropertyItemSize doesn't set it! EndIf If @error Then Return SetError(@error, @extended, -1) If $aResult[0] Then Return SetError(10, $aResult[0], -1) Return $aResult[3] EndFunc ;==>__GDIPlus_ImageGetPropertyItemSize ; #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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 2: $a1PropertyItem is not a 4-element 1-d array ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 101: identifier is not an integer ; 201: size is not an integer ; 202: size cannot be number of values for ASCII string and undefined types ; 202: size is zero ; 301: type is illegal ; 401: value array is either not a 1-d array or has no elements ; 402: size is incompatible with type ; 403: size cannot be negative for ASCII string and undefined types ; 404: Numerator and denominator are required for rational types ; 405: size and number of elements in value array differ ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then Return SetError(2, 0, False) EndIf Local $iId = $a1PropertyItem[0] Local $iLength = $a1PropertyItem[1] Local $iType = $a1PropertyItem[2] Local $a1values = $a1PropertyItem[3] If Not IsInt($iId) Then Return SetError(101, 0, False) EndIf If Not IsInt($iLength) Then Return SetError(201, 0, False) EndIf If $iLength = 0 Then Return SetError(202, 0, False) EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _ $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ; do nothing Case Else Return SetError(301, 0, False) EndSwitch If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then Return SetError(401, 0, False) EndIf 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 If BitAND($iLength, 1) <> 0 Then Return SetError(402, 0, False) EndIf $iqValues = Int($iLength / 2) Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _ $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction If BitAND($iLength, 3) <> 0 Then Return SetError(402, 0, False) EndIf $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(403, 0, False) 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 $iBytes = -$iLength EndSwitch $iqValues = -$iLength EndIf If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then If BitAND($iqValues, 1) <> 0 Then Return SetError(404, 0, False) EndIf EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL If UBound($a1values)<>$iqValues Then Return SetError(405,0,False) EndIf EndSwitch 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 I discovered that GdipGetPropertySize returns 2 ($GDIP_ERRINVALIDPARAMETER) if an image has no properties. $GDIP_ERRPROPERTYNOTFOUND would be more logical. I added this specific error code to the Help for cGDIPlus_ImageGetAllPropertyItems() Edited May 19, 2018 by c.haslam Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
jpm Posted May 19, 2018 Posted May 19, 2018 for me is it not good to have the same @error = 1à the one after the size return and the other @error +10 should stay it allows to differentiate whe the error come from
c.haslam Posted May 19, 2018 Author Posted May 19, 2018 It is up to you as a developer. Looking through GDIPlus.au3, I see that, for a user error. some functions return 0 (False) and others return -1. Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
c.haslam Posted May 22, 2018 Author Posted May 22, 2018 If __GDIPlus_ImageGetPropertyItemSize() returns -1 and does not set an error, c_GDIPlus_ImageGetPropertyItemSize() will need to be changed. GdipGetPropertyItemSize returns 0 even if the property item does not exist. Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
KaFu Posted October 12, 2018 Posted October 12, 2018 Hi Team, looks promising :), but changed properties seem not to be saved with _GDIPlus_ImageSaveToFile(), or do I miss something? Maybe it's necessary to pass it along with the encoder params in_GDIPlus_ImageSaveToFileEx()? Best Regards OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
c.haslam Posted October 12, 2018 Author Posted October 12, 2018 It has been a while since I was into this subject. I think that you are correct. I temporarily abandoned this project because of that, and because my code or the UDFs were causing a failure elsewhere in my script: something that worked until I got into changing properties. It seemed that memory was being overwritten. My script is several thousand lines, and I did not find a way of reproducing the problem in a short script. All I can say is that something is wrong somewhere. I was new to structs at the time, so spent a lot of time trying to discover whether I was misusing them. KaFu 1 Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
KaFu Posted October 12, 2018 Posted October 12, 2018 Ah, me bad . Quote GDI+ does not allow you to save an image to the same file that you used to construct the image. Works fine ... expandcollapse popup#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 ;------------------------------------------------- _GDIPlus_Startup() Local $hImageF = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\test.jpg") ; http://www.exiv2.org/tags.html Local $a_PropertyValue = ['Cropped with BIC'] ; 0x9286 = Exif.Photo.UserComment Local $a_PropertyItem = [0x9286, 40, 2, $a_PropertyValue] cGDIPlus_ImageSetPropertyItem($hImageF, $a_PropertyItem) _GDIPlus_ImageSaveToFile($hImageF, @ScriptDir & "\test2.jpg") ; GDI+ does not allow you to save an image to the same file that you used to construct the image. _GDIPlus_ImageDispose($hImageF) _GDIPlus_Shutdown() ShellExecute(@ScriptDir & "\test2.jpg") ; #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: ; 1: DllCall failed. Common cause: _GIDPlus_Startup() has not been called ; 2: $a1PropertyItem is not a 4-element 1-d array ; 10: @extended may contain GPSTATUS error code ($GPID_ERR* see GDIPlusConstants.au3). ; 101: identifier is not an integer ; 201: size is not an integer ; 202: size cannot be number of values for ASCII string and undefined types ; 202: size is zero ; 301: type is illegal ; 401: value array is either not a 1-d array or has no elements ; 402: size is incompatible with type ; 403: size cannot be negative for ASCII string and undefined types ; 404: Numerator and denominator are required for rational types ; 405: size and number of elements in value array differ ; 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 ;+ ; For list of EXIF property items, see https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html ; + and https://www.media.mit.edu/pia/Research/deepview/exif.html ; 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) If (Not IsArray($a1PropertyItem)) Or UBound($a1PropertyItem, 0) <> 1 Or UBound($a1PropertyItem) <> 4 Then Return SetError(2, 0, False) EndIf Local $iId = $a1PropertyItem[0] Local $iLength = $a1PropertyItem[1] Local $iType = $a1PropertyItem[2] Local $a1values = $a1PropertyItem[3] If Not IsInt($iId) Then Return SetError(101, 0, False) EndIf If Not IsInt($iLength) Then Return SetError(201, 0, False) EndIf If $iLength = 0 Then Return SetError(202, 0, False) EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPEBYTE, $GDIP_PROPERTYTAGTYPEASCII, $GDIP_PROPERTYTAGTYPESHORT, _ $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPEUNDEFINED, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL ; do nothing Case Else Return SetError(301, 0, False) EndSwitch If (Not IsArray($a1values)) Or UBound($a1values, 0) <> 1 Or UBound($a1values) = 0 Then Return SetError(401, 0, False) EndIf 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 If BitAND($iLength, 1) <> 0 Then Return SetError(402, 0, False) EndIf $iqValues = Int($iLength / 2) Case $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, $GDIP_PROPERTYTAGTYPESLONG, _ $GDIP_PROPERTYTAGTYPESRATIONAL ;Array of UInt / Fraction If BitAND($iLength, 3) <> 0 Then Return SetError(402, 0, False) EndIf $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(403, 0, False) 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 $iBytes = -$iLength EndSwitch $iqValues = -$iLength EndIf If $iType = $GDIP_PROPERTYTAGTYPERATIONAL Or $iType = $GDIP_PROPERTYTAGTYPERATIONAL Then If BitAND($iqValues, 1) <> 0 Then Return SetError(404, 0, False) EndIf EndIf Switch $iType Case $GDIP_PROPERTYTAGTYPESHORT, $GDIP_PROPERTYTAGTYPELONG, $GDIP_PROPERTYTAGTYPERATIONAL, _ $GDIP_PROPERTYTAGTYPESLONG, $GDIP_PROPERTYTAGTYPESRATIONAL If UBound($a1values) <> $iqValues Then Return SetError(405, 0, False) EndIf EndSwitch 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 OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
littlebigman Posted June 4, 2023 Posted June 4, 2023 Hello, I need to recurse through a directory, grab the lat+lon of each JPG file therein, and check if it was shot within 100m from a reference location. Can this UDF read the values of GPSLatitude + GPSLongitude? Thank you. #include <GUIConstantsEx.au3> #include <ListViewConstants.au3> #include <GDIPlus.au3> #include <File.au3> ;NO GPS #include "image_get_info.au3" ;https://www.autoitscript.com/forum/topic/13096-udf-imagegetinfo/ #include <GDIPlus.au3> _GDIPlus_Startup() Local $hImage = _GDIPlus_ImageLoadFromFile("test.jpg") If @error Then _GDIPlus_Shutdown() ConsoleWrite("An error has occured - unable to load image!") Exit EndIf Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage) ;_ArrayDisplay($aPropID) Local $aValues For $i = 1 To $aPropID[0][0] $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) ;GPSLatitude + GPSLongitude? ;_ArrayDisplay($aValues, $aPropID[$i][1]) ConsoleWrite($aPropID[$i][1] & @CRLF) Next _GDIPlus_ImageDispose($hImage) _GDIPlus_Shutdown()
KaFu Posted June 4, 2023 Posted June 4, 2023 Yes it can. To calculate the distance use something like this: https://www.movable-type.co.uk/scripts/latlong.html expandcollapse popup#include <GUIConstantsEx.au3> #include <ListViewConstants.au3> #include <GDIPlus.au3> #include <File.au3> ;NO GPS #include "image_get_info.au3" ;https://www.autoitscript.com/forum/topic/13096-udf-imagegetinfo/ #include <GDIPlus.au3> _GDIPlus_Startup() Local $hImage = _GDIPlus_ImageLoadFromFile("test.jpg") If @error Then _GDIPlus_Shutdown() ConsoleWrite("An error has occured - unable to load image!") Exit EndIf Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage) ;_ArrayDisplay($aPropID) Local $aValues, $GpsLatitudeRef, $GpsLongitudeRef For $i = 1 To $aPropID[0][0] ;GPSLatitude + GPSLongitude? Switch $aPropID[$i][1] Case "GpsLatitudeRef" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then $GpsLatitudeRef = $aValues[1] EndIf Case "GpsLongitudeRef" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then $GpsLongitudeRef = $aValues[1] EndIf Case "GpsLatitude" Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then For $y = 1 To $aValues[0] $sOutput &= $aValues[$y] & "." Next EndIf $sOutput = $aPropID[$i][1] & " = " & $GpsLatitudeRef & " " & StringTrimRight($sOutput, 1) ConsoleWrite($sOutput & @CRLF) Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) And $aValues[0] = 3 Then Local $GpsLatitude_Decimal For $y = 1 To $aValues[0] Switch $y Case 1 $GpsLatitude_Decimal = $aValues[$y] Case 2 $GpsLatitude_Decimal += ($aValues[$y] / 60) Case 3 $GpsLatitude_Decimal += ($aValues[$y] / 3600) EndSwitch Next EndIf $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLatitudeRef & " " & $GpsLatitude_Decimal ConsoleWrite($sOutput & @CRLF & @CRLF) Case "GpsLongitude" Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then For $y = 1 To $aValues[0] $sOutput &= $aValues[$y] & "." Next EndIf $sOutput = $aPropID[$i][1] & " = " & $GpsLongitudeRef & " " & StringTrimRight($sOutput, 1) ConsoleWrite($sOutput & @CRLF) Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) And $aValues[0] = 3 Then Local $GpsLongitude_Decimal For $y = 1 To $aValues[0] Switch $y Case 1 $GpsLongitude_Decimal = $aValues[$y] Case 2 $GpsLongitude_Decimal += ($aValues[$y] / 60) Case 3 $GpsLongitude_Decimal += ($aValues[$y] / 3600) EndSwitch Next EndIf $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLongitudeRef & " " & $GpsLongitude_Decimal ConsoleWrite($sOutput & @CRLF & @CRLF) EndSwitch Next _GDIPlus_ImageDispose($hImage) _GDIPlus_Shutdown() littlebigman 1 OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
KaFu Posted June 4, 2023 Posted June 4, 2023 Here are some examples for calculating the distance. OS: Win10-22H2 - 64bit - German, AutoIt Version: 3.3.16.1, AutoIt Editor: SciTE, Website: https://funk.eu AMT - Auto-Movie-Thumbnailer (2024-Oct-13) BIC - Batch-Image-Cropper (2023-Apr-01) COP - Color Picker (2009-May-21) DCS - Dynamic Cursor Selector (2024-Oct-13) HMW - Hide my Windows (2024-Oct-19) HRC - HotKey Resolution Changer (2012-May-16) ICU - Icon Configuration Utility (2018-Sep-16) SMF - Search my Files (2025-May-18) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16)
littlebigman Posted June 4, 2023 Posted June 4, 2023 Thanks. For others' benefit, here's some code that seems to work to find geotagged pictures that were shot within 100 meters from a reference location: expandcollapse popup#include <GUIConstantsEx.au3> #include <ListViewConstants.au3> #include <GDIPlus.au3> #include <File.au3> #include <Math.au3> ;https://stackoverflow.com/questions/37484988/distance-formula-between-two-lat-longs-autoit Func _distanceInKm($lat1, $lon1, $lat2, $lon2) Local $iRadius = 6371 Local $iLat = _Radian($lat2 - $lat1) Local $iLon = _Radian($lon2- $lon1) Local $a = Sin($iLat / 2) * Sin($iLat / 2) + Cos(_Radian($lat1)) * Cos(_Radian($lat2)) * Sin($iLon / 2) * Sin($iLon / 2) Local $c = 2 * ATan2(Sqrt($a), Sqrt(1 - $a)) Local $d = $iRadius * $c Return Abs($d) EndFunc Func ATan2($y, $x) Return (2 * ATan($y / ($x + Sqrt($x * $x + $y * $y)))) EndFunc Local Const $REF_LAT = 1.23, $REF_LON = 4.56 Local Const $RADIUS = 100 ; meters Local $distance ; meters ConsoleWrite("Let's get going" & @CRLF) Local $sFileSelectFolder = FileSelectFolder("Select a folder", "") If @error Then Exit MsgBox($MB_SYSTEMMODAL, "", "No folder was selected.") Local $aArray = _FileListToArrayRec($sFileSelectFolder, "*.jpg", $FLTAR_FILES , $FLTAR_RECUR, $FLTAR_NOSORT,$FLTAR_FULLPATH ) ;_ArrayDisplay($aArray, "JPG") If UBound($aArray) = 0 Then Exit ConsoleWrite("zero elements") ;1D array only _GDIPlus_Startup() Local $filename, $OSM_URL = "https://www.openstreetmap.org/?" For $picindx = 1 to UBound($aArray) -1 $filename = $aArray[$picindx] Local $hImage = _GDIPlus_ImageLoadFromFile($filename) If @error Then _GDIPlus_Shutdown() ConsoleWrite("An error has occured - unable to load image! " & $filename) Exit EndIf Local $aPropID = _GDIPlus_ImageGetPropertyIdList($hImage) ;_ArrayDisplay($aPropID) Local $aValues, $GpsLatitudeRef, $GpsLongitudeRef For $i = 1 To $aPropID[0][0] Switch $aPropID[$i][1] Case "GpsLatitudeRef" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then $GpsLatitudeRef = $aValues[1] EndIf Case "GpsLongitudeRef" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then $GpsLongitudeRef = $aValues[1] EndIf Case "GpsLatitude" Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then For $y = 1 To $aValues[0] $sOutput &= $aValues[$y] & "." Next EndIf ;~ $sOutput = $aPropID[$i][1] & " = " & $GpsLatitudeRef & " " & StringTrimRight($sOutput, 1) ;~ ConsoleWrite($sOutput & @CRLF) Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) And $aValues[0] = 3 Then Local $GpsLatitude_Decimal For $y = 1 To $aValues[0] Switch $y Case 1 $GpsLatitude_Decimal = $aValues[$y] Case 2 $GpsLatitude_Decimal += ($aValues[$y] / 60) Case 3 $GpsLatitude_Decimal += ($aValues[$y] / 3600) EndSwitch Next EndIf $sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLatitudeRef & " " & $GpsLatitude_Decimal ;ConsoleWrite($sOutput & @CRLF & @CRLF) ;ConsoleWrite($GpsLatitude_Decimal & ",") Case "GpsLongitude" Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) Then For $y = 1 To $aValues[0] $sOutput &= $aValues[$y] & "." Next EndIf ;~ $sOutput = $aPropID[$i][1] & " = " & $GpsLongitudeRef & " " & StringTrimRight($sOutput, 1) ;~ ConsoleWrite($sOutput & @CRLF) Local $sOutput = "" $aValues = _GDIPlus_ImageGetPropertyItem($hImage, $aPropID[$i][0]) If IsArray($aValues) And $aValues[0] = 3 Then Local $GpsLongitude_Decimal For $y = 1 To $aValues[0] Switch $y Case 1 $GpsLongitude_Decimal = $aValues[$y] Case 2 $GpsLongitude_Decimal += ($aValues[$y] / 60) Case 3 $GpsLongitude_Decimal += ($aValues[$y] / 3600) EndSwitch Next EndIf ;$sOutput = $aPropID[$i][1] & "Decimal = " & $GpsLongitudeRef & " " & $GpsLongitude_Decimal $OSM_URL &= StringFormat("mlat=%s&mlon=%s",$GpsLatitude_Decimal,$GpsLongitude_Decimal) $distance =Int(1000 * _distanceInKm($REF_LAT, $REF_LON, $GpsLatitude_Decimal, $GpsLongitude_Decimal)) If $distance <= $RADIUS Then ;picture taken within $RADIUS of reference location? ConsoleWrite($filename & @CRLF) ConsoleWrite("Distance : " & $distance & @CRLF) ConsoleWrite($OSM_URL & @CRLF & @CRLF) EndIf $OSM_URL = "https://www.openstreetmap.org/?" ;reset EndSwitch Next _GDIPlus_ImageDispose($hImage) Next _GDIPlus_Shutdown() ConsoleWrite("Done." & @CRLF) KaFu 1
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now