Blog. Just Blog

Работа с метаданными изображений с помощью GDI+

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Работа с метаданными изображений с помощью GDI+
Работа с метаданными изображений с помощью GDI+

Библиотека Windows GDI+ предназначена для работы с графикой и мультимедиа и включает в себя более 600 различных функций для этого. Особо радует поддержка: все ключевые функции доступны уже начиная с Windows XP. Ранее мы уже немного касались этой темы. Сегодня разберем примеры, как средствами GDI+ можно получить метаданные из изображений различных форматов и изменить их.

Для работы нам понадобятся несколько структур, достаточно обширный список тегов и константы с идентификаторами типов тегов.
  1. ; Структура для работы с GDI+
  2. struct GdiplusStartupInput
  3.     GdiplusVersion           dd ?
  4.     DebugEventCallback       dd ?
  5.     SuppressBackgroundThread dd ?
  6.     SuppressExternalCodecs   dd ?
  7. ends
  8.  
  9. ; Структура для работы с тегами
  10. struct PropertyItem
  11.     id     dd ?
  12.     length dd ?
  13.     type   dw ?
  14.            dw ?
  15.     value  dd ?
  16. ends
В процессе у меня возникли непонятки по структуре PropertyItem. Если сложить размерность ее полей, заявленных в официальной документации, то ее размер получается 14 байт, а указатель на данные value должен начинаться на 10-м байте в памяти от начала структуры. Фактически же структура PropertyItem занимает в памяти 16 байт и указатель на данные начинается с 12-го байта. Как выяснилось, поля структуры должны быть выровнены до границы DWORD, поэтому для обеспечения корректной адресации мне пришлось добавить в нее дополнительный безымянный WORD.
  1. PropertyTagArtist=0x013B
  2. PropertyTagBitsPerSample=0x0102
  3. PropertyTagCellHeight=0x0109
  4. PropertyTagCellWidth=0x0108
  5. PropertyTagChrominanceTable=0x5091
  6. PropertyTagColorMap=0x0140
  7. PropertyTagColorTransferFunction=0x501A
  8. PropertyTagCompression=0x0103
  9. PropertyTagCopyright=0x8298
  10. PropertyTagDateTime=0x0132
  11. PropertyTagDocumentName=0x010D
  12. PropertyTagDotRange=0x0150
  13. PropertyTagEquipMake=0x010F
  14. PropertyTagEquipModel=0x0110
  15. PropertyTagExifAperture=0x9202
  16. PropertyTagExifBrightness=0x9203
  17. PropertyTagExifCfaPattern=0xA302
  18. PropertyTagExifColorSpace=0xA001
  19. PropertyTagExifCompBPP=0x9102
  20. PropertyTagExifCompConfig=0x9101
  21. PropertyTagExifContrast=0xA408
  22. PropertyTagExifCustomRendered=0xA401
  23. PropertyTagExifDTDigSS=0x9292
  24. PropertyTagExifDTDigitized=0x9004
  25. PropertyTagExifDTOrig=0x9003
  26. PropertyTagExifDTOrigSS=0x9291
  27. PropertyTagExifDTSubsec=0x9290
  28. PropertyTagExifDeviceSettingDesc=0xA40B
  29. PropertyTagExifDigitalZoomRatio=0xA404
  30. PropertyTagExifExposureBias=0x9204
  31. PropertyTagExifExposureIndex=0xA215
  32. PropertyTagExifExposureMode=0xA402
  33. PropertyTagExifExposureProg=0x8822
  34. PropertyTagExifExposureTime=0x829A
  35. PropertyTagExifFNumber=0x829D
  36. PropertyTagExifFPXVer=0xA000
  37. PropertyTagExifFileSource=0xA300
  38. PropertyTagExifFlash=0x9209
  39. PropertyTagExifFlashEnergy=0xA20B
  40. PropertyTagExifFocalLength=0x920A
  41. PropertyTagExifFocalLengthIn35mmFilm=0xA405
  42. PropertyTagExifFocalResUnit=0xA210
  43. PropertyTagExifFocalXRes=0xA20E
  44. PropertyTagExifFocalYRes=0xA20F
  45. PropertyTagExifGainControl=0xA407
  46. PropertyTagExifIFD=0x8769
  47. PropertyTagExifISOSpeed=0x8827
  48. PropertyTagExifInterop=0xA005
  49. PropertyTagExifLightSource=0x9208
  50. PropertyTagExifMakerNote=0x927C
  51. PropertyTagExifMaxAperture=0x9205
  52. PropertyTagExifMeteringMode=0x9207
  53. PropertyTagExifOECF=0x8828
  54. PropertyTagExifPixXDim=0xA002
  55. PropertyTagExifPixYDim=0xA003
  56. PropertyTagExifRelatedWav=0xA004
  57. PropertyTagExifSaturation=0xA409
  58. PropertyTagExifSceneCaptureType=0xA406
  59. PropertyTagExifSceneType=0xA301
  60. PropertyTagExifSensingMethod=0xA217
  61. PropertyTagExifSharpness=0xA40A
  62. PropertyTagExifShutterSpeed=0x9201
  63. PropertyTagExifSpatialFR=0xA20C
  64. PropertyTagExifSpectralSense=0x8824
  65. PropertyTagExifSubjectArea=0x9214
  66. PropertyTagExifSubjectDist=0x9206
  67. PropertyTagExifSubjectDistanceRange=0xA40C
  68. PropertyTagExifSubjectLoc=0xA214
  69. PropertyTagExifUniqueImageID=0xA420
  70. PropertyTagExifUserComment=0x9286
  71. PropertyTagExifVer=0x9000
  72. PropertyTagExifWhiteBalance=0xA403
  73. PropertyTagExtraSamples=0x0152
  74. PropertyTagFillOrder=0x010A
  75. PropertyTagFrameDelay=0x5100
  76. PropertyTagFreeByteCounts=0x0121
  77. PropertyTagFreeOffset=0x0120
  78. PropertyTagGamma=0x0301
  79. PropertyTagGlobalPalette=0x5102
  80. PropertyTagGpsAltitude=0x0006
  81. PropertyTagGpsAltitudeRef=0x0005
  82. PropertyTagGpsAreaInformation=0x001C
  83. PropertyTagGpsDate=0x001D
  84. PropertyTagGpsDestBear=0x0018
  85. PropertyTagGpsDestBearRef=0x0017
  86. PropertyTagGpsDestDist=0x001A
  87. PropertyTagGpsDestDistRef=0x0019
  88. PropertyTagGpsDestLat=0x0014
  89. PropertyTagGpsDestLatRef=0x0013
  90. PropertyTagGpsDestLong=0x0016
  91. PropertyTagGpsDestLongRef=0x0015
  92. PropertyTagGpsDifferential=0x001E
  93. PropertyTagGpsGpsDop=0x000B
  94. PropertyTagGpsGpsMeasureMode=0x00A
  95. PropertyTagGpsGpsSatellites=0x0008
  96. PropertyTagGpsGpsStatus=0x0009
  97. PropertyTagGpsGpsTime=0x0007
  98. PropertyTagGpsIFD=0x8825
  99. PropertyTagGpsImgDir=0x0011
  100. PropertyTagGpsImgDirRef=0x0010
  101. PropertyTagGpsLatitude=0x0002
  102. PropertyTagGpsLatitudeRef=0x0001
  103. PropertyTagGpsLongitude=0x0004
  104. PropertyTagGpsLongitudeRef=0x0003
  105. PropertyTagGpsMapDatum=0x0012
  106. PropertyTagGpsProcessingMethod=0x001B
  107. PropertyTagGpsSpeed=0x000D
  108. PropertyTagGpsSpeedRef=0x000C
  109. PropertyTagGpsTrack=0x000F
  110. PropertyTagGpsTrackRef=0x000E
  111. PropertyTagGpsVer=0x0000
  112. PropertyTagGrayResponseCurve=0x0123
  113. PropertyTagGrayResponseUnit=0x0122
  114. PropertyTagGridSize=0x5011
  115. PropertyTagHalftoneDegree=0x500C
  116. PropertyTagHalftoneHints=0x0141
  117. PropertyTagHalftoneLPI=0x500A
  118. PropertyTagHalftoneLPIUnit=0x500B
  119. PropertyTagHalftoneMisc=0x500E
  120. PropertyTagHalftoneScreen=0x500F
  121. PropertyTagHalftoneShape=0x500D
  122. PropertyTagHostComputer=0x013C
  123. PropertyTagICCProfile=0x8773
  124. PropertyTagICCProfileDescriptor=0x0302
  125. PropertyTagImageDescription=0x010E
  126. PropertyTagImageHeight=0x0101
  127. PropertyTagImageTitle=0x0320
  128. PropertyTagImageWidth=0x0100
  129. PropertyTagIndexBackground=0x5103
  130. PropertyTagIndexTransparent=0x5104
  131. PropertyTagInkNames=0x014D
  132. PropertyTagInkSet=0x014C
  133. PropertyTagJPEGACTables=0x0209
  134. PropertyTagJPEGDCTables=0x0208
  135. PropertyTagJPEGInterFormat=0x0201
  136. PropertyTagJPEGInterLength=0x0202
  137. PropertyTagJPEGLosslessPredictors=0x0205
  138. PropertyTagJPEGPointTransforms=0x0206
  139. PropertyTagJPEGProc=0x0200
  140. PropertyTagJPEGQTables=0x0207
  141. PropertyTagJPEGQuality=0x5010
  142. PropertyTagJPEGRestartInterval=0x0203
  143. PropertyTagLoopCount=0x5101
  144. PropertyTagLuminanceTable=0x5090
  145. PropertyTagMaxSampleValue=0x0119
  146. PropertyTagMinSampleValue=0x0118
  147. PropertyTagNewSubfileType=0x00FE
  148. PropertyTagNumberOfInks=0x014E
  149. PropertyTagOrientation=0x0112
  150. PropertyTagPageName=0x011D
  151. PropertyTagPageNumber=0x0129
  152. PropertyTagPaletteHistogram=0x5113
  153. PropertyTagPhotometricInterp=0x0106
  154. PropertyTagPixelPerUnitX=0x5111
  155. PropertyTagPixelPerUnitY=0x5112
  156. PropertyTagPixelUnit=0x5110
  157. PropertyTagPlanarConfig=0x011C
  158. PropertyTagPredictor=0x013D
  159. PropertyTagPrimaryChromaticities=0x013F
  160. PropertyTagPrintFlags=0x5005
  161. PropertyTagPrintFlagsBleedWidth=0x5008
  162. PropertyTagPrintFlagsBleedWidthScale=0x5009
  163. PropertyTagPrintFlagsCrop=0x5007
  164. PropertyTagPrintFlagsVersion=0x5006
  165. PropertyTagREFBlackWhite=0x0214
  166. PropertyTagResolutionUnit=0x0128
  167. PropertyTagResolutionXLengthUnit=0x5003
  168. PropertyTagResolutionXUnit=0x5001
  169. PropertyTagResolutionYLengthUnit=0x5004
  170. PropertyTagResolutionYUnit=0x5002
  171. PropertyTagRowsPerStrip=0x0116
  172. PropertyTagSMaxSampleValue=0x0155
  173. PropertyTagSMinSampleValue=0x0154
  174. PropertyTagSRGBRenderingIntent=0x0303
  175. PropertyTagSampleFormat=0x0153
  176. PropertyTagSamplesPerPixel=0x0115
  177. PropertyTagSoftwareUsed=0x0131
  178. PropertyTagStripBytesCount=0x0117
  179. PropertyTagStripOffsets=0x0111
  180. PropertyTagSubfileType=0x00FF
  181. PropertyTagT4Option=0x0124
  182. PropertyTagT6Option=0x0125
  183. PropertyTagTargetPrinter=0x0151
  184. PropertyTagThreshHolding=0x0107
  185. PropertyTagThumbnailArtist=0x5034
  186. PropertyTagThumbnailBitsPerSample=0x5022
  187. PropertyTagThumbnailColorDepth=0x5015
  188. PropertyTagThumbnailCompressedSize=0x5019
  189. PropertyTagThumbnailCompression=0x5023
  190. PropertyTagThumbnailCopyRight=0x503B
  191. PropertyTagThumbnailData=0x501B
  192. PropertyTagThumbnailDateTime=0x5033
  193. PropertyTagThumbnailEquipMake=0x5026
  194. PropertyTagThumbnailEquipModel=0x5027
  195. PropertyTagThumbnailFormat=0x5012
  196. PropertyTagThumbnailHeight=0x5014
  197. PropertyTagThumbnailImageDescription=0x5025
  198. PropertyTagThumbnailImageHeight=0x5021
  199. PropertyTagThumbnailImageWidth=0x5020
  200. PropertyTagThumbnailOrientation=0x5029
  201. PropertyTagThumbnailPhotometricInterp=0x5024
  202. PropertyTagThumbnailPlanarConfig=0x502F
  203. PropertyTagThumbnailPlanes=0x5016
  204. PropertyTagThumbnailPrimaryChromaticities=0x5036
  205. PropertyTagThumbnailRawBytes=0x5017
  206. PropertyTagThumbnailRefBlackWhite=0x503A
  207. PropertyTagThumbnailResolutionUnit=0x5030
  208. PropertyTagThumbnailResolutionX=0x502D
  209. PropertyTagThumbnailResolutionY=0x502E
  210. PropertyTagThumbnailRowsPerStrip=0x502B
  211. PropertyTagThumbnailSamplesPerPixel=0x502A
  212. PropertyTagThumbnailSize=0x5018
  213. PropertyTagThumbnailSoftwareUsed=0x5032
  214. PropertyTagThumbnailStripBytesCount=0x502C
  215. PropertyTagThumbnailStripOffsets=0x5028
  216. PropertyTagThumbnailTransferFunction=0x5031
  217. PropertyTagThumbnailWhitePoint=0x5035
  218. PropertyTagThumbnailWidth=0x5013
  219. PropertyTagThumbnailYCbCrCoefficients=0x5037
  220. PropertyTagThumbnailYCbCrPositioning=0x5039
  221. PropertyTagThumbnailYCbCrSubsampling=0x5038
  222. PropertyTagTileByteCounts=0x0145
  223. PropertyTagTileLength=0x0143
  224. PropertyTagTileOffset=0x0144
  225. PropertyTagTileWidth=0x0142
  226. PropertyTagTransferFuncition=0x012D
  227. PropertyTagTransferRange=0x0156
  228. PropertyTagWhitePoint=0x013E
  229. PropertyTagXPosition=0x011E
  230. PropertyTagXResolution=0x011A
  231. PropertyTagYCbCrCoefficients=0x0211
  232. PropertyTagYCbCrPositioning=0x0213
  233. PropertyTagYCbCrSubsampling=0x0212
  234. PropertyTagYPosition=0x011F
  235. PropertyTagYResolution=0x011B
Конечно, список тегов этим не ограничивается. Почти у каждого производителя фотокамер есть свои наборы тегов со своими числовыми значениями. Более подробно о тегах и их значениях можно почитать на сайте замечательной утилиты ExifTool.
  1. PropertyTagTypeByte      = 1
  2. PropertyTagTypeASCII     = 2
  3. PropertyTagTypeShort     = 3
  4. PropertyTagTypeLong      = 4
  5. PropertyTagTypeRational  = 5
  6. PropertyTagTypeUndefined = 7
  7. PropertyTagTypeSLONG     = 9
  8. PropertyTagTypeSRational = 10
В зависимости от своего предназначения, каждый тег имеет определенный тип. Это могут быть строки, последовательность байт, слов или двойных слов. Еще есть особый тип Rational, который представляет собой пару из целочисленного делимого и делителя, а значением такого тега, соответственно, будет вещественное число, получаемое от деления этих чисел. Такой формат используется, например, для записи GPS-координат.

Переходим к работе с изображением. Инициализируем GDI+ и загружаем графический файл. Путь к файлу обязательно должен быть в юникоде.
  1.         ; Инициализация GDI+
  2.         mov     [gdiplusSInput.GdiplusVersion],1
  3.         mov     [gdiplusSInput.DebugEventCallback],0
  4.         mov     [gdiplusSInput.SuppressBackgroundThread],0
  5.         mov     [gdiplusSInput.SuppressExternalCodecs],0
  6.         invoke  GdiplusStartup,gdiplusToken,gdiplusSInput,0
  7.  
  8.         ; Загрузить изображение
  9.         invoke  GdipLoadImageFromFile,fname,hImage
Для получения метаданных используется несколько функций. При помощи функции GdipGetPropertyCount получаем количество тегов, которые GDI+ может извлечь из файла. Внутренняя структура графических файлов весьма разнообразна, в разных полях могут храниться разные метаданные и не все из них доступны штатными средствами системы. Функция GdipGetPropertyIdList возвращает массив идентификаторов доступных тегов. Дальше в цикле перебираем этот массив, функцией GdipGetPropertyItemSize получаем размер памяти, необходимой для извлечения данных, резервируем ее, затем функцией GdipGetPropertyItem извлекаем структуру PropertyItem по каждому тегу.
  1.         ; Получить количество тегов, содержащихся в метаданных
  2.         invoke  GdipGetPropertyCount,[hImage],nCount
  3.         mov     eax,[nCount]
  4.         ; Необходимый размер памяти
  5.         shl     eax,2
  6.         invoke  GdipAlloc,eax
  7.         mov     [pMeta],eax
  8.  
  9.         ; Получить список тегов из файла
  10.         invoke  GdipGetPropertyIdList,[hImage],[nCount],[pMeta]
  11.         mov     esi,[pMeta]
  12. loc_get_tag:
  13.         lodsd
  14.         mov     [nTag],eax
  15.         push    esi
  16.  
  17.         ; Получить размер данных тега
  18.         invoke  GdipGetPropertyItemSize,[hImage],[nTag],nSize
  19.         or      eax,eax
  20.         jnz     loc_next
  21.  
  22.         mov     eax,[nSize]
  23.         invoke  GdipAlloc,eax
  24.         mov     ebx,eax
  25.  
  26.         ; Получить значение тега
  27.         invoke  GdipGetPropertyItem,[hImage],[nTag],[nSize],ebx
  28.         ...
  29.         ...
  30.         ; Действия с полученным тегом
  31.         ; [ebx+PropertyItem.id] -> идентификатор тега
  32.         ; [ebx+PropertyItem.type] -> тип тега
  33.         ; [ebx+PropertyItem.length] -> размер данных
  34.         ; [ebx+PropertyItem.value] -> указатель на данные
  35.         ...
  36.         ...
  37.         invoke  GdipFree,ebx
  38. loc_next:
  39.         pop     esi
  40.         dec     [nCount]
  41.         cmp     [nCount],0
  42.         ja      loc_get_tag
  43.  
  44.         invoke  GdipFree,[pMeta]
Если идентификаторы тегов заранее известны, то получение списка тегов можно пропустить. Приведенный ниже пример извлекает из файла строковое значение названия модели камеры и числовое значение ISO.
  1.         ; Модель камеры
  2.         invoke  GdipGetPropertyItemSize,[hImage],\
  3.                 PropertyTagEquipModel,nSize
  4.         mov     eax,[nSize]
  5.         shl     eax,1
  6.         invoke  GdipAlloc,eax
  7.         mov     ebx,eax
  8.         invoke  GdipGetPropertyItem,[hImage],\
  9.                 PropertyTagEquipModel,[nSize],ebx
  10.         ; [ebx+PropertyItem.value] -> указатель на строку с моделью камеры
  11.         ; [ebx+PropertyItem.length] -> длина строки
  12.         invoke  GdipFree,ebx
  13.  
  14.         ; ISO
  15.         invoke  GdipGetPropertyItemSize,[hImage],\
  16.                 PropertyTagExifISOSpeed,nSize
  17.         mov     eax,[nSize]
  18.         shl     eax,1
  19.         invoke  GdipAlloc,eax
  20.         mov     ebx,eax
  21.         invoke  GdipGetPropertyItem,[hImage],\
  22.                 PropertyTagExifISOSpeed,[nSize],ebx
  23.         mov     eax,[ebx+PropertyItem.value]
  24.         ; Этот параметр приходит в формате WORD, надо преобразовать в DWORD
  25.         movzx   eax,word[eax]
  26.         ; EAX -> значение ISO при съемке
  27.         invoke  GdipFree,ebx
Важное замечание. В результате экспериментов выяснилось, что если EXIF в файле JPEG записан в формате Little-endian, то функции GDI+ извлекают из него не все теги. Может быть это не единственное и не обязательное условие, но случаи неполного извлечения тегов на моей практике теперь точно есть. Так что если требуется полная и качественная поддержка информации из EXIF, то придется парсить его самостоятельно.

Для изменения метаданных используется функция GdipSetPropertyItem. Перед ее вызовом надо указать идентификатор изменяемого тега и настроить его новые значения в зависимости от типа. В следующем примере будет заменено название производителя камеры и встроенная превьюшка файла.
  1.         ; Настроить новое значение тега EquipMake
  2.         mov     [prop.id],PropertyTagEquipMake
  3.         mov     [prop.type],PropertyTagTypeASCII
  4.         invoke  lstrlen,szNew
  5.         ; Длина строки с учетом завершающего нулевого символа
  6.         inc     eax
  7.         mov     [prop.length],eax
  8.         mov     [prop.value],szNew
  9.         ; Установить новое значение тега
  10.         invoke  GdipSetPropertyItem,[hImage],prop
  11.  
  12.         ; Настроить новое значение тега ThumbnailData
  13.         mov     [prop.id],PropertyTagThumbnailData
  14.         mov     [prop.type],PropertyTagTypeByte
  15.         ; Размер файла превьюшки
  16.         mov     [prop.length],th_len
  17.         ; Содержимое превьюшки
  18.         mov     [prop.value],tn
  19.         ; Установить новое значение тега
  20.         invoke  GdipSetPropertyItem,[hImage],prop
Обратите внимание, что для строковых данных их длина указывается с учетом завершающего нуля. Для пустой строки, соответственно, длина будет равняться 1 байту.

Добавление новых тегов выполняется точно так же. Удалять ненужные теги вроде как можно при помощи функции GdipRemovePropertyItem, однако на практике она у меня так и не заработала как надо. В загруженном изображении данные удаляются нормально, это подтверждает ошибочный результат функции GdipGetPropertyItemSize, но при сохранении в файл удаленные теги остаются на месте. Более того, если тег сперва изменить, а потом удалить, то в файл будет записано первоначальное значение этого тега. Очередная непонятка, на которую у меня нет ответа.

Перед тем, как сохранить изображение в файл, нужно получить CLSID кодировщика, формат которого будет задействован. Я вынес код в отдельную вспомогательную функцию, так как она может еще не раз пригодиться в других примерах по работе с графикой GDI+.
  1. ;----------------------------------------------------------
  2. ; Получение CLSID кодировщика нужного mime-type
  3. ;----------------------------------------------------------
  4. ; На входе:
  5. ;     pEncoder - указатель на строку mime-type (unicode)
  6. ;     pCLSID - указатель на 16-байтный буфер для CLSID
  7. ; На выходе:
  8. ;     EAX=1 - CLSID успешно получен
  9. ;     EAX=0 - произошла какая-то ошибка
  10. ;----------------------------------------------------------
  11. proc GetEncoderCLSID pEncoder:DWORD, pCLSID:DWORD
  12.         struct _ImageCodecInfo
  13.             Clsid             rb 16
  14.             FormatID          rb 16
  15.             CodecName         dd ?
  16.             DllName           dd ?
  17.             FormatDescription dd ?
  18.             FilenameExtension dd ?
  19.             MimeType          dd ?
  20.             Flags             dd ?
  21.             Version           dd ?
  22.             SigCount          dd ?
  23.             SizeSize          dd ?
  24.             SigPattern        dd ?
  25.             SigMask           dd ?
  26.         ends
  27.  
  28.         locals
  29.             encoders_count dd ?
  30.             encoders_size  dd ?
  31.             result         dd ?
  32.         endl
  33.  
  34.         pusha
  35.  
  36.         ; По умолчанию результат - ошибка
  37.         mov     [result],0
  38.  
  39.         ; Получить размер памяти для списка кодировщиков
  40.         lea     eax,[encoders_size]
  41.         push    eax
  42.         lea     eax,[encoders_count]
  43.         push    eax
  44.         invoke  GdipGetImageEncodersSize
  45.         test    eax,eax
  46.         jnz     .loc_ret
  47.         ; Выделить память
  48.         invoke  VirtualAlloc,0,[encoders_size],\
  49.                 MEM_COMMIT,PAGE_READWRITE
  50.         test    eax,eax
  51.         jz      .loc_ret
  52.         mov     ebx,eax
  53.         ; Получить список кодировщиков
  54.         invoke  GdipGetImageEncoders,[encoders_count],\
  55.                 [encoders_size],ebx
  56.         test    eax,eax
  57.         jnz     .loc_clear
  58.         ; Поочередно сравнить mime-type полученных
  59.         ; кодировщиков с искомым
  60. .scan_encoders:
  61.         mov     esi,[ebx+_ImageCodecInfo.MimeType]
  62.         mov     edi,[pEncoder]
  63. @@:
  64.         lodsw
  65.         cmp     ax,0
  66.         je      .encoder_found
  67.         inc     edi
  68.         inc     edi
  69.         cmp     ax,word[edi-2]
  70.         je      @b
  71.         ; Следующий кодировщик
  72.         add     ebx,sizeof._ImageCodecInfo
  73.         dec     [encoders_count]
  74.         jnz     .scan_encoders
  75.         jmp     .loc_clear
  76. .encoder_found:
  77.         ; Скопировать CLSID найденного кодировщика
  78.         lea     esi,[ebx+_ImageCodecInfo.Clsid]
  79.         mov     edi,[pCLSID]
  80.         movsd
  81.         movsd
  82.         movsd
  83.         movsd
  84.         ; Успешный результат
  85.         mov     [result],1
  86.  
  87. .loc_clear:
  88.         ; Прибраться за собой
  89.         invoke  VirtualFree,ebx,0,MEM_RELEASE
  90. .loc_ret:
  91.         popa
  92.         mov     eax,[result]
  93.         ret
  94. endp
На входе передается указатель на юникодную строку с mime-type нужного формата, например, "image/jpeg" или "image/bmp", на выходе получаем заполненный буфер с CLSID кодировщика, если он установлен в системе. В принципе, можно попробовать обойтись меньшими движениями, просто захардкодив CLSID нужных кодировщиков, но для серьезных проектов это плохая практика. Во-первых, не факт, что MS оставят CLSID неизменными навсегда, а во-вторых, по какой-то причине кодировщика может вообще не оказаться в системе. Вероятность любого из этих событий ничтожно мала, но все-таки больше нуля.
  1. ; mime-type кодировщика
  2. encoder       du 'image/jpeg',0
  3. encoder_clsid rb 16
  4.         ...
  5.         ...
  6.         ; Получить CLSID кодировщика для image/jpeg
  7.         stdcall GetEncoderCLSID,encoder,encoder_clsid
  8.  
  9.         ; Сохранить изменения в новый файл
  10.         invoke  GdipSaveImageToFile,[hImage],nname,encoder_clsid,NULL
В результате экспериментов также было выявлено, что новый EXIF в JPEG-файлы всегда сохраняется в формате Big-endian. Измененные и/или добавленные теги становятся доступны для повторной обработки.

Осталось прибраться за собой. После чтения или изменения метаданных освобождаем все задействованные ресурсы:
  1.         ; Освободить графику
  2.         invoke  GdipDisposeImage,[hImage]
  3.  
  4.         ; Выключение GDI+
  5.         invoke  GdiplusShutdown,[gdiplusToken]
В приложении примеры программ с исходными текстами, первая из которых показывает несколько тегов из графического файла, вторая сохраняет в лог все без исключения теги с их значениями, а третья меняет в EXIF значения тегов производителя, модели фотокамеры и превьюшку.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Image.Metadata.Demo.zip (28,468 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 982 | Комментариев: 10

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
gob (22.11.2022 в 19:35):
хех, даже из png печатает текст в UTF-8 верно. А комментарии в jpg не извлекаются
ManHunter (19.12.2021 в 11:13):
Полно редакторов тегов, от консольного ExifTool до всяких гуишных комбайнов. Недавно тут один выкладывал на исследование. По запросу "каталогизатор фото" гуглояндексы тоже выдают немало полезного.
Barm (19.12.2021 в 10:34):
Приветствую! Ассоциативно тут вопрос созрел чутка оффтопный:
Фотки и видосы с фотоаппарата я сохраняю папками по месяцам. И в разные месяцы там может быть природа, город, семья, животные и т.д.
Уже который раз думаю, как бы удобнее всего подобных тегов напрописывать в фотографии и видосы, чтобы потом при необходимости легко найти все фото и видео (в моём случае JPG и MOV) во всех этих ежемесячных папках?
ManHunter (07.12.2021 в 16:11):
Сайт ExifTool ожил
ManHunter (29.11.2021 в 11:54):
Я имел в виду начало структуры.
align 4
struct abc
   a dw ?
   b db ?
   c dd ?
ends
в этом случае само местоположение структуры (но не ее полей!!!) в памяти будет выровнено.

А для выравнивания полей придется лепить что-то типа
struct abc
   a dw ?
     dw ?
   b db ?
     rb 3
   c dd ?
ends

или
struct abc
   a rw 2
   b rb 4
   c dd ?
ends

Это FASM, сишные и MASMовские трюки тут не прокатывают
Nemo (29.11.2021 в 11:47):
Выравнивание структуры - и есть выравнивание полей
В С директивой
struct s{
#pragma align 8 /* поле a выравнено по 8-байтной границе адреса */
   int a;
   int bar;

#pragma align 16 /* поле b выровнено по 16-байтной границе адреса */
   int b;
} t[2];
ManHunter (29.11.2021 в 11:44):
В этом-то и дело, что в доках не пишут. В лучшем случае упоминают про выравнивание самой структуры, а не ее полей. Значит я правильно подумал.

ЦитатаПроцессоры работают эффективнее, когда имеют дело с правильно выровненными данными. Например, значение типа WORD всегда должно начинаться с четного адреса, кратного 2, значение типа DWORD - с четного адреса, кратного 4, и т.д.

Джефри Рихтер "Создание эффективных Win-32 приложений с учетом специфики 64 разрядной версии Windows"
Nemo (29.11.2021 в 11:41):
>> Если верить официальной документации, то ее размер получается 14 байт, а
>> указатель на данные value должен начинаться на 10-м байте в памяти от
>> начала структуры. Фактически же структура PropertyItem занимает в
>> памяти 16 байт указатель на данные начинается с 12-го байта

Выравнивание полей структуры на DWORD. В доках не везде пишут (и не все структуры выравниваются)

В синтаcисе МАСМ:

str STRUCT 4 ; (для win32 или 8 for x64)
   A DW ? ; offset 0
   b DW ? ; offset 4
   c DW ? ; offset 8
   d dd ? ; offset 12
str ENDS
ManHunter (29.11.2021 в 11:21):
Вот ничоси, сайт ExifTools реально не работает. Пару дней назад проверял, все было нормально. Подождем, может Фил просто хостинг не продлил или еще чего-то.
NeshAliNehrin (29.11.2021 в 11:15):
Доброго дня.
Проверял кликабельность ссылки на ExifTools (от 29.11.21 не рабочая),
возможно пригодится другая: https://github.com/exiftool/exiftool

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0054 сек.) / Память: 4.5 Mb
Наверх