Parsix Posted June 30, 2022 Share Posted June 30, 2022 Hi How to Copy MetaData (Exif And more) from a Photo (Jpeg) to Another Photo (Jpeg) ? Link to comment Share on other sites More sharing options...
Parsix Posted June 30, 2022 Author Share Posted June 30, 2022 expandcollapse popup#include <GDIPlus.au3> #include <WinAPIHObj.au3> Local $sfile = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)"); _Clone_MetaData($sfile) Func _Clone_MetaData($sfile, $sMasterfile = @ScriptDir & "\IMG_Base.JPG") Local $hImage1, $hImage2, $hGraphic If Not FileExists($sfile) Then Return 0 If Not FileExists($sMasterfile) Then Return 0 ; Initialize GDI+ library _GDIPlus_Startup() $hImage2 = _GDIPlus_ImageLoadFromFile($sfile) $hImage1 = _GDIPlus_ImageLoadFromFile($sMasterfile) Local $aDim = _GDIPlus_ImageGetDimension ( $hImage1 ) Local $iXUR = $aDim[0] Local $iYDR = $aDim[1] ; Draw one image in another $hGraphic = _GDIPlus_ImageGetGraphicsContext($hImage1) _GDIPlus_GraphicsSetInterpolationMode ( $hGraphic, 7 ) _GDIPlus_DrawImagePoints($hGraphic, $hImage2, 0, 0, $iXUR, 0, 0, $iYDR) ; Save resultant image Local $sNewFile = FileSaveDialog("Choose a filename.", @ScriptDir, "JPEG files (*.jpg;*.jpeg)", 2, "IMG_Cloned.JPG" _GDIPlus_ImageSaveToFile($hImage1, $sNewFile) ; Clean up resources _GDIPlus_ImageDispose($hImage1) _GDIPlus_ImageDispose($hImage2) ; Shut down GDI+ library _GDIPlus_Shutdown() If FileExists($sNewFile) Then ShellExecute($sNewFile) EndFunc ;==>_Clone_MetaData i use this sample for clone metadata, i need a better solution Link to comment Share on other sites More sharing options...
Musashi Posted June 30, 2022 Share Posted June 30, 2022 2 hours ago, Parsix said: i use this sample for clone metadata, i need a better solution Possibly, the use of the well-known command line tool exiftool (by Phil Harvey) would be a suitable solution. exiftool -TagsFromFile fromImage.jpg toImage.jpg Additional info on -TagsFromFile, see for example : https://exiftool.org/forum/index.php?topic=10189.0 Parsix 1 "In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move." Link to comment Share on other sites More sharing options...
Parsix Posted June 30, 2022 Author Share Posted June 30, 2022 1 minute ago, Musashi said: Possibly, the use of the well-known command line tool exiftool (by Phil Harvey) would be a suitable solution. exiftool -TagsFromFile fromImage.jpg toImage.jpg Additional info on -TagsFromFile, see for example : https://exiftool.org/forum/index.php?topic=10189.0 thanks, do you have a solution without exiftool (any external app or dll) ? Link to comment Share on other sites More sharing options...
Musashi Posted June 30, 2022 Share Posted June 30, 2022 35 minutes ago, Parsix said: thanks, do you have a solution without exiftool (any external app or dll) ? Unfortunately, no external app or dll I have worked with so far. I use exiftool only for removing metadata from images, but I know, that the tool has many more powerful features. The command line variant does not even need to be installed. By the way : what difficulties do you expect regarding the use of exiftool ? Parsix 1 "In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move." Link to comment Share on other sites More sharing options...
Parsix Posted June 30, 2022 Author Share Posted June 30, 2022 I'm looking to use the Autoit programming language talent instead of the side plugin command line Accurate copy and reproduction of metadata in the new file Link to comment Share on other sites More sharing options...
Solution KaFu Posted July 1, 2022 Solution Share Posted July 1, 2022 (edited) I use something like this in my program BIC (as a fallback for the prerfered ExifTool method). Although I too prefer ExifTool over this, as GDI JPG processing is not lossless. I process the files, save them as PNG, use an external tool called jpge.exe to encode a jpg from the png and then set the Exif infos with ExifTool. ; jpg with GDI always bad ; https://www.autoitscript.com/forum/topic/187573-perplexed-by-jpg-conversion-result/ ; "The problem (after reading your edits) is probably due to the fact that GDI+ uses 4:1:1 subsampling for JPG files in it's default Encoder." ; The issue is that GDI+ enables chroma subsampling which modifies the color values accordingly. expandcollapse popup#include <GDIPlus.au3> #include <WinAPIHObj.au3> ; Initialize GDI+ library _GDIPlus_Startup() Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)") ; If @error Then Exit Local $sfile_output = StringReplace($sfile_input, ".", "_Out.") _Clone_MetaData($sfile_input, $sfile_output) If FileExists($sfile_output) Then ShellExecute($sfile_output) ; Shut down GDI+ library _GDIPlus_Shutdown() Func _Clone_MetaData($sfile_input, $sfile_output) If Not FileExists($sfile_input) Then Return 0 Local $a_DateTimeOriginal = FileGetTime($sfile_input) Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5] Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input) Local $aDim = _GDIPlus_ImageGetDimension($hImage) Local $a_Local_Input_EXIF_Dimensions[2] $a_Local_Input_EXIF_Dimensions[0] = $aDim[0] $a_Local_Input_EXIF_Dimensions[1] = $aDim[1] Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage) Local $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $aDim[0], $aDim[1], $GDIP_PXF32ARGB) If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties) Local $sString = "Code ripped from BIC, https://funk.eu" Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0131, $sString, 2, StringLen($sString) + 1) ; Software > 2=ASCII If IsArray($a_Local_Input_EXIF_Dimensions) Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0100, $a_Local_Input_EXIF_Dimensions[0], 4, -1) ; ImageWidth > 4=UINT EndIf If IsArray($a_Local_Input_EXIF_Dimensions) Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0101, $a_Local_Input_EXIF_Dimensions[1], 4, -1) ; ImageHeight > 4=UINT EndIf $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0132, $sString, 2, StringLen($sString) + 1) ; 0x0132 ModifyDate > 2=ASCII $sString = "Custom User Comment" $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9286, $sString, 2, StringLen($sString) + 1) ; 0x9286 UserComment > 2=ASCII If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1) ; 0x9003 DateTimeOriginal > 2=ASCII _GDIPlus_ImageSaveToFile($hClone, $sfile_output) ; Clean up resources _GDIPlus_ImageDispose($hImage) _GDIPlus_BitmapDispose($hClone) EndFunc ;==>_Clone_MetaData Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties) For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1 Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]] cGDIPlus_ImageSetPropertyItem($hClone, $aInput) Next EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Array Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength) ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html #cs 0x0131 > Software 0x010e > ImageDescription 0x9286 > UserComment 0x0132 > ModifyDate 0x9003 > DateTimeOriginal string ExifIFD (date/time when original image was taken) #ce Local $a_PropertyValue = [$s_PropertyItem_Value] Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue] Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem) Return SetError(@error, @extended, $iRes) EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Ex ; #FUNCTION# ==================================================================================================================== ; Name ..........: cGDIPlus_ImageSetPropertyItem ; Description ...: Sets a specified property item (piece of meta data) for an Image object ; Syntax ........: cGDIPlus_ImageSetPropertyItem($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) ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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) ; _ConsoleWrite($iId & @TAB & $iType & @CRLF) 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 ; #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 ; #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") ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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 Edited July 1, 2022 by KaFu Parsix, Musashi and UEZ 3 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 (2024-Oct-20) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16) Link to comment Share on other sites More sharing options...
Parsix Posted July 1, 2022 Author Share Posted July 1, 2022 (edited) 7 hours ago, KaFu said: I use something like this in my program BIC (as a fallback for the prerfered ExifTool method). Although I too prefer ExifTool over this, as GDI JPG processing is not lossless. I process the files, save them as PNG, use an external tool called jpge.exe to encode a jpg from the png and then set the Exif infos with ExifTool. ; jpg with GDI always bad ; https://www.autoitscript.com/forum/topic/187573-perplexed-by-jpg-conversion-result/ ; "The problem (after reading your edits) is probably due to the fact that GDI+ uses 4:1:1 subsampling for JPG files in it's default Encoder." ; The issue is that GDI+ enables chroma subsampling which modifies the color values accordingly. expandcollapse popup#include <GDIPlus.au3> #include <WinAPIHObj.au3> ; Initialize GDI+ library _GDIPlus_Startup() Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)") ; If @error Then Exit Local $sfile_output = StringReplace($sfile_input, ".", "_Out.") _Clone_MetaData($sfile_input, $sfile_output) If FileExists($sfile_output) Then ShellExecute($sfile_output) ; Shut down GDI+ library _GDIPlus_Shutdown() Func _Clone_MetaData($sfile_input, $sfile_output) If Not FileExists($sfile_input) Then Return 0 Local $a_DateTimeOriginal = FileGetTime($sfile_input) Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5] Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input) Local $aDim = _GDIPlus_ImageGetDimension($hImage) Local $a_Local_Input_EXIF_Dimensions[2] $a_Local_Input_EXIF_Dimensions[0] = $aDim[0] $a_Local_Input_EXIF_Dimensions[1] = $aDim[1] Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage) Local $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $aDim[0], $aDim[1], $GDIP_PXF32ARGB) If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties) Local $sString = "Code ripped from BIC, https://funk.eu" Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0131, $sString, 2, StringLen($sString) + 1) ; Software > 2=ASCII If IsArray($a_Local_Input_EXIF_Dimensions) Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0100, $a_Local_Input_EXIF_Dimensions[0], 4, -1) ; ImageWidth > 4=UINT EndIf If IsArray($a_Local_Input_EXIF_Dimensions) Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0101, $a_Local_Input_EXIF_Dimensions[1], 4, -1) ; ImageHeight > 4=UINT EndIf $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x0132, $sString, 2, StringLen($sString) + 1) ; 0x0132 ModifyDate > 2=ASCII $sString = "Custom User Comment" $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9286, $sString, 2, StringLen($sString) + 1) ; 0x9286 UserComment > 2=ASCII If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hClone, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1) ; 0x9003 DateTimeOriginal > 2=ASCII _GDIPlus_ImageSaveToFile($hClone, $sfile_output) ; Clean up resources _GDIPlus_ImageDispose($hImage) _GDIPlus_BitmapDispose($hClone) EndFunc ;==>_Clone_MetaData Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties) For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1 Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]] cGDIPlus_ImageSetPropertyItem($hClone, $aInput) Next EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Array Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength) ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html #cs 0x0131 > Software 0x010e > ImageDescription 0x9286 > UserComment 0x0132 > ModifyDate 0x9003 > DateTimeOriginal string ExifIFD (date/time when original image was taken) #ce Local $a_PropertyValue = [$s_PropertyItem_Value] Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue] Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem) Return SetError(@error, @extended, $iRes) EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Ex ; #FUNCTION# ==================================================================================================================== ; Name ..........: cGDIPlus_ImageSetPropertyItem ; Description ...: Sets a specified property item (piece of meta data) for an Image object ; Syntax ........: cGDIPlus_ImageSetPropertyItem($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) ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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) ; _ConsoleWrite($iId & @TAB & $iType & @CRLF) 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 ; #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 ; #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") ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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 Very nice can be resized with keep quality and metadat? Edited July 1, 2022 by Parsix Link to comment Share on other sites More sharing options...
KaFu Posted July 3, 2022 Share Posted July 3, 2022 As the picture is re-encoded, JPG is not lossless and you can not determine the initial quality setting of a JPG, the quality will always be reduced. expandcollapse popup#include <GDIPlus.au3> #include <WinAPIHObj.au3> #include <Array.au3> ; Initialize GDI+ library _GDIPlus_Startup() Local $sfile_input = FileOpenDialog("Please select file", "", "JPEG files (*.jpg;*.jpeg)") If @error Then Exit Local $sfile_output = StringReplace($sfile_input, ".", "_Out.") _Clone_MetaData($sfile_input, $sfile_output, Default, 500) If FileExists($sfile_output) Then ShellExecute($sfile_output) ; Shut down GDI+ library _GDIPlus_Shutdown() Func _Clone_MetaData($sfile_input, $sfile_output, $iWidth = Default, $iHeight = Default, $i_JPG_Quality = 95) If Not FileExists($sfile_input) Then Return 0 Local $a_DateTimeOriginal = FileGetTime($sfile_input) Local $s_DateTimeOriginal = $a_DateTimeOriginal[0] & ":" & $a_DateTimeOriginal[1] & ":" & $a_DateTimeOriginal[2] & " " & $a_DateTimeOriginal[3] & ":" & $a_DateTimeOriginal[4] & ":" & $a_DateTimeOriginal[5] Local $hImage = _GDIPlus_ImageLoadFromFile($sfile_input) Local $aDim = _GDIPlus_ImageGetDimension($hImage) Local $a_Local_Input_EXIF_Properties = cGDIPlus_ImageGetAllPropertyItems($hImage) Local $i_Local_Input_EXIF_Properties_Orientation = 1 For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1 If $a_Local_Input_EXIF_Properties[$i][0] = 0x0112 Then ; Orientation $i_Local_Input_EXIF_Properties_Orientation = $a_Local_Input_EXIF_Properties[$i][3] $i_Local_Input_EXIF_Properties_Orientation = $i_Local_Input_EXIF_Properties_Orientation[0] ExitLoop EndIf Next Switch $i_Local_Input_EXIF_Properties_Orientation Case 5 To 9 ConsoleWrite("! Exif orientation applied" & @CRLF) Local $iBuffer = $aDim[0] $aDim[0] = $aDim[1] $aDim[1] = $iBuffer EndSwitch Local $iRatio = $aDim[0] / $aDim[1] If $iWidth <> Default Or $iHeight <> Default Then If $iWidth = Default Then $iWidth = $iHeight / $iRatio EndIf If $iHeight = Default Then $iHeight = $iWidth * $iRatio EndIf Local $hBitmap_Scaled = _GDIPlus_ImageResize($hImage, $iWidth, $iHeight) ConsoleWrite($aDim[0] & @TAB & $aDim[1] & @TAB & $aDim[0] / $aDim[1] & @TAB & $iWidth & @TAB & $iHeight & @TAB & $hBitmap_Scaled & @CRLF) _GDIPlus_ImageDispose($hImage) $hImage = $hBitmap_Scaled EndIf If IsArray($a_Local_Input_EXIF_Properties) Then _GDIPlus_ImageSetPropertyItem_Array($hImage, $a_Local_Input_EXIF_Properties) Local $sString = "Code ripped from BIC, https://funk.eu" Local $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0131, $sString, 2, StringLen($sString) + 1) ; Software > 2=ASCII $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0100, $iWidth, 4, -1) ; ImageWidth > 4=UINT $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0101, $iHeight, 4, -1) ; ImageHeight > 4=UINT $sString = @YEAR & ":" & @MON & ":" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x0132, $sString, 2, StringLen($sString) + 1) ; 0x0132 ModifyDate > 2=ASCII $sString = "Original Image Dimensions = " & $aDim[0] & "x" & $aDim[1] $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x9286, $sString, 2, StringLen($sString) + 1) ; 0x9286 UserComment > 2=ASCII If $s_DateTimeOriginal Then $iRes = _GDIPlus_ImageSetPropertyItem_Ex($hImage, 0x9003, $s_DateTimeOriginal, 2, StringLen($s_DateTimeOriginal) + 1) ; 0x9003 DateTimeOriginal > 2=ASCII Local $sCLSID = _GDIPlus_EncodersGetCLSID("JPG") Local $tParams = _GDIPlus_ParamInit(1) Local $tData = DllStructCreate("int Quality") DllStructSetData($tData, "Quality", $i_JPG_Quality) Local $pData = DllStructGetPtr($tData) _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData) Local $pParams = DllStructGetPtr($tParams) _GDIPlus_ImageSaveToFileEx($hImage, $sfile_output, $sCLSID, $pParams) ; Clean up resources _GDIPlus_ImageDispose($hImage) EndFunc ;==>_Clone_MetaData Func _GDIPlus_ImageSetPropertyItem_Array($hClone, $a_Local_Input_EXIF_Properties) For $i = 0 To UBound($a_Local_Input_EXIF_Properties) - 1 Local $aInput = [$a_Local_Input_EXIF_Properties[$i][0], $a_Local_Input_EXIF_Properties[$i][1], $a_Local_Input_EXIF_Properties[$i][2], $a_Local_Input_EXIF_Properties[$i][3]] cGDIPlus_ImageSetPropertyItem($hClone, $aInput) Next EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Array Func _GDIPlus_ImageSetPropertyItem_Ex($hImage, $i_PropertyItem_Number, $s_PropertyItem_Value, $iType, $iLength) ; https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html #cs 0x0131 > Software 0x010e > ImageDescription 0x9286 > UserComment 0x0132 > ModifyDate 0x9003 > DateTimeOriginal string ExifIFD (date/time when original image was taken) #ce Local $a_PropertyValue = [$s_PropertyItem_Value] Local $a_PropertyItem = [$i_PropertyItem_Number, $iLength, $iType, $a_PropertyValue] Local $iRes = cGDIPlus_ImageSetPropertyItem($hImage, $a_PropertyItem) Return SetError(@error, @extended, $iRes) EndFunc ;==>_GDIPlus_ImageSetPropertyItem_Ex ; #FUNCTION# ==================================================================================================================== ; Name ..........: cGDIPlus_ImageSetPropertyItem ; Description ...: Sets a specified property item (piece of meta data) for an Image object ; Syntax ........: cGDIPlus_ImageSetPropertyItem($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) ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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) ; _ConsoleWrite($iId & @TAB & $iType & @CRLF) 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 ; #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 ; #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") ;--------- should be moved to GDIPlusConstants.au3 Local 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 ;------------------------------------------------- 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 Parsix 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 (2024-Oct-20) - THE file info and duplicates search tool SSD - Set Sound Device (2017-Sep-16) Link to comment Share on other sites More sharing options...
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