c.haslam Posted April 18, 2018 Posted April 18, 2018 (edited) 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: expandcollapse popup; #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: expandcollapse popup#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 April 23, 2018 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
c.haslam Posted April 22, 2018 Author Posted April 22, 2018 (edited) And here is cGDIPlus_ImageSetPropertyItem, the inverse of cGDIPlus_ImageGetPropertyItem expandcollapse popup; #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: expandcollapse popupFunc 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 April 25, 2018 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
c.haslam Posted April 23, 2018 Author Posted April 23, 2018 (edited) 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: expandcollapse popup; #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: expandcollapse popupFunc 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 April 23, 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
c.haslam Posted April 23, 2018 Author Posted April 23, 2018 (edited) 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 April 23, 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
UEZ Posted April 23, 2018 Posted April 23, 2018 (edited) @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 April 23, 2018 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
c.haslam Posted April 23, 2018 Author Posted April 23, 2018 UEZ, Good to hear from you! No rush -- and a I am sure that your have much else to do! Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
c.haslam Posted April 23, 2018 Author Posted April 23, 2018 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
c.haslam Posted April 24, 2018 Author Posted April 24, 2018 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
c.haslam Posted April 26, 2018 Author Posted April 26, 2018 (edited) 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 May 9, 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
UEZ Posted May 9, 2018 Posted May 9, 2018 (edited) @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+. Edited May 9, 2018 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
UEZ Posted May 9, 2018 Posted May 9, 2018 (edited) What I did so far: expandcollapse popup#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 May 11, 2018 by UEZ added JPM's suggestions KaFu 1 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
jpm Posted May 10, 2018 Posted May 10, 2018 (edited) error checking is not perfect can those functions replace the current UDF standard without script breaking 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 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 May 10, 2018 by Melba23 Code tags and not quote
jpm Posted May 10, 2018 Posted May 10, 2018 for non script Breaking the best can be cGDIPlus_ImageGetPropertyItem -> _GDIPlus_ImageGetPropertyItemEX for the other cGDI* _GDI* as they are new
UEZ Posted May 10, 2018 Posted May 10, 2018 thanks @jpm. I updated the code above accordingly. 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
c.haslam Posted May 11, 2018 Author Posted May 11, 2018 Would you like me to add more error checking, or will UEZ do it? Spoiler CDebug Dumps values of variables including arrays and DLL structs, to a GUI, to the Console, and to the Clipboard
UEZ Posted May 11, 2018 Posted May 11, 2018 Is there any need to more error checks? 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!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ
c.haslam Posted May 11, 2018 Author Posted May 11, 2018 I see no need for more error checks. 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 12, 2018 Author Posted May 12, 2018 (edited) 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 May 12, 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 13, 2018 Posted May 13, 2018 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
c.haslam Posted May 13, 2018 Author Posted May 13, 2018 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
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