Что нового

Извлечение и конвертация иконок, их объединение

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Решил показать скрипт, который писал для того, чтобы выполнить просьбу CreatoR`а (читайте эту тему, начиная с Переводчик Google, Ответ #12).

Что и зачем делает скрипт:
Есть нужные иконки .ico, содержащие несколько размеров. Нам нужны на выходе иконки только одного размера в формате .ico и в формате .png, а также файл .png, содержащий все эти изображения.

Так как писал на AutoIt v3.3.8.1, то в поздних версиях надо разбираться с файлами #include. Сделал две версии.

Код для AutoIt v3.3.8.1.
Код:
#include <WinAPIEx.au3>
#include <GDIPlus.au3>
#include <File.au3>

Opt('MustDeclareVars', 1)

Global $iSize = 16, $iARGB = 32; размер иконок, которые будем извлекать, и их битность (32 bit RGB + Alpha)

Global $sDirSources = @ScriptDir & '\IcoSources\', $sDirResIco = @ScriptDir & '\Ico16x16\', $sDirResPng16x16 = @ScriptDir & '\Png16x16\', _
		$sResPngBig = @ScriptDir & '\PngBig\PngBig.png', $aIcoFiles, $aTmp, $hIcon, $hBitmap, $tBits, $pBits, $hBigBitmap, $hGraphic, $sTxt

Global $iTimer = TimerInit()

;~ получаем в массив исходные файлы иконок
$aIcoFiles = _FileListToArray($sDirSources, '*.ico', 1)
If @error Then Exit 1
For $i = 1 To $aIcoFiles[0]
	$hIcon = 1
;~ 	получаем информацию о размерах иконок и их битность. См. описание _EnumIconFile http://autoit-script.ru/index.php?topic=10788.0
	$aTmp = _EnumIconFile($sDirSources & $aIcoFiles[$i])
	If @error Then Exit 2
;~ 	проверяем иконки на наличие размера ($iSize х $iSize) и на битность ($iARGB) для конвертации в png
	For $j = 1 To $aTmp[0][0]
		If $aTmp[$j][0] = $iSize Then
			$hIcon = 0
			If ($aTmp[$j][1] <> $iSize) Or ($aTmp[$j][2] <> $iARGB) Then Exit 3
		EndIf
	Next
	If $hIcon Then Exit 3
Next
$aTmp = 1
;~ инициируем GDI+
_GDIPlus_Startup()
Do
;~ 	Создаем прозрачный битмап с нужным размером для объединенной картинки $sResPngBig
	$hBigBitmap = _GDIPlus_BitmapCreateFromScan0($iSize * $aIcoFiles[0], $iSize)
	If @error Then ExitLoop
;~ 	Получаем его контекст
	$hGraphic = _GDIPlus_ImageGetGraphicsContext($hBigBitmap)
	If @error Then ExitLoop
	$aTmp = 0
Until 1
If $aTmp Then
;~ 	если ошибка, то удаляеем битмап и выход
	If $hBigBitmap Then _GDIPlus_ImageDispose($hBigBitmap)
	_GDIPlus_Shutdown()
	Exit 4
EndIf
$tBits = DllStructCreate('byte[' & (4 * $iSize ^ 2) & ']')
$pBits = DllStructGetPtr($tBits)
;~ текст с информацией для дальнейшего использования полученных файлов
$sTxt = 'Local $a_Country[' & $aIcoFiles[0] + 1 & '][4] = [[' & $aIcoFiles[0] & '], _' & @CRLF
For $i = 1 To $aIcoFiles[0]
;~ 	извлекаем иконку нужного размера
	$hIcon = _WinAPI_ShellExtractIcon($sDirSources & $aIcoFiles[$i], 0, $iSize, $iSize)
	If @error Then Exit 5
;~ 	сохраняем иконку в файл с новым именем (по номеру)
	_WinAPI_SaveHICONToFile($sDirResIco & $i & '.ico', $hIcon)
	If @error Then ExitLoop
;~ 	получаем информацию об иконке
	$aTmp = _WinAPI_GetIconInfo($hIcon)
	If @error Then ExitLoop
;~ 	копируем нужную информацию в структуру $tBits
	_WinAPI_GetBitmapBits($aTmp[5], 4 * $iSize ^ 2, $pBits)
	If @error Then ExitLoop
;~ 	создаем из $tBits битмап на прозрачном фоне
	$hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize, 4 * $iSize, $GDIP_PXF32ARGB, $pBits)
	If @error Then ExitLoop
	For $j = 4 To 5
;~ 		удаляеем то, что уже не нужно
		If Not _WinAPI_DeleteObject($aTmp[$j]) Then ExitLoop 2
	Next
;~ 	сохраняем битмап в файл png с новым именем (по номеру)
	If Not _GDIPlus_ImageSaveToFile($hBitmap, $sDirResPng16x16 & $i & '.png') Then ExitLoop
;~ 	рисуем битмап иконки на прозрачном битмапе (файл png $sResPngBig)
	If Not _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, ($i - 1) * $iSize, 0, $iSize, $iSize) Then ExitLoop
;~ 	удаляеем то, что уже не нужно
	If Not _GDIPlus_ImageDispose($hBitmap) Then ExitLoop
;~ 	удаляеем то, что уже не нужно
	If Not _WinAPI_DestroyIcon($hIcon) Then ExitLoop
	$aTmp = 0
	$hBitmap = 0
	$hIcon = 0
;~ 	добавляем к тексту новую строку с именем файла, удалив расширение, и его индексом
	$sTxt &= '['''', ''' & _WinAPI_PathRemoveExtension($aIcoFiles[$i]) & ''', '''', ' & $i & '], _' & @CRLF
Next
$tBits = 0
$pBits = 0
;~ если ошибка, то удаляеем все
If ($hIcon) Or ($hBitmap) Or (UBound($aTmp) = 6) Then
	$pBits = 1
	If $hIcon Then _WinAPI_DestroyIcon($hIcon)
	If $hBitmap Then _GDIPlus_ImageDispose($hBitmap)
	If UBound($aTmp) Then
		For $j = 4 To 5
			_WinAPI_DeleteObject($aTmp[$j])
		Next
	EndIf
Else
;~ 	сохраняем результат в файл png $sResPngBig
	$tBits = _GDIPlus_ImageSaveToFile($hBigBitmap, $sResPngBig)
EndIf
;~ удаляем все и освобождаем GDI+
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_ImageDispose($hBigBitmap)
_GDIPlus_Shutdown()
If $pBits Then
	MsgBox(16, 'Error', 'Error')
	Exit 13
Else
	$sTxt = StringTrimRight($sTxt, 5) & ']' & @CRLF
	ConsoleWrite($sTxt & @LF);текст можно сразу записать в au3 файл для дальнейшего использования
	MsgBox(64, 'Info', TimerDiff($iTimer) & @LF & 'Save PngBig: ' & $tBits)
EndIf

; #FUNCTION# ====================================================================================================================
; Name...........: _EnumIconFile
; Description....: Enumerates an icons from the specified .ico file.
; Syntax.........: _EnumIconFile ( $sFile )
; Parameters.....: $sFile  - The path to the .ico file whose icons are to be enumerated.
; Return values..: Success - The 2D array containing the following information:
;
;                            [0][0] - Number of rows in array (n)
;                            [0][i] - Unused
;                            [n][0] - The width of the icon, in pixels.
;                            [n][1] - The heigth of the icon, in pixels.
;                            [n][2] - The color depth of the icon, in bits-per-pixel.
;                            [n][3] - 1 (TRUE) if the icon has a PNG compression (Windows Vista+), or 0 (FALSE) otherwise.
;
;                  Failure - 0 and sets the @error flag to non-zero.
; Author.........: Yashied
; Modified.......:
; Remarks........: None
; Related........:
; Link...........: http://autoit-script.ru/index.php?topic=10788.0
; Example........:
; ===============================================================================================================================
Func _EnumIconFile($sFile)

	Local $hFile, $tEntry, $tHeader, $tData, $pData, $pIcon, $Bytes, $Count, $Offset, $Enum = 0

	$hFile = _WinAPI_CreateFileEx($sFile, 3, 0x80000000, 0x03)
	If @error Then
		Return SetError(1, 0, 0)
	EndIf
	Do
		$Bytes = _WinAPI_GetFileSizeEx($hFile)
		If Not $Bytes Then
			ExitLoop
		EndIf
		$tData = DllStructCreate('byte[' & $Bytes & ']')
		$pData = DllStructGetPtr($tData)
		If Not _WinAPI_ReadFile($hFile, $pData, $Bytes, $Bytes) Then
			ExitLoop
		EndIf
		$tHeader = DllStructCreate('ushort;ushort;ushort', $pData)
		$Count = DllStructGetData($tHeader, 3)
		If Not $Count Then
			ExitLoop
		EndIf
		Dim $Enum[$Count + 1][4] = [[$Count]]
		For $i = 1 To $Count
			$tEntry = DllStructCreate('byte;byte;byte;byte;ushort;ushort;long;long', $pData + 6 + 16 * ($i - 1))
			$Offset = DllStructGetData($tEntry, 8)
			$pIcon = $pData + $Offset
			$Enum[$i][2] = DllStructGetData($tEntry, 6)
			If DllStructGetData(DllStructCreate('byte[8]', $pIcon), 1) = Binary('0x89504E470D0A1A0A') Then
				; PNG => Retrieve IHDR chunk data (always first chunk, offset = 8)
				$tHeader = DllStructCreate('dword;dword;byte;byte;byte;byte;byte', $pIcon + 16)
				$Enum[$i][0] = _WinAPI_SwapDWord(DllStructGetData($tHeader, 1))
				$Enum[$i][1] = _WinAPI_SwapDWord(DllStructGetData($tHeader, 2))
				$Enum[$i][3] = 1
			Else
				; ICO => Retrieve BITMAPINFOHEADER structure
				$tHeader = DllStructCreate($tagBITMAPINFOHEADER, $pIcon)
				$Enum[$i][0] = DllStructGetData($tHeader, 2)
				$Enum[$i][1] = DllStructGetData($tHeader, 3) / 2
				$Enum[$i][3] = 0
			EndIf
		Next
	Until 1
	_WinAPI_CloseHandle($hFile)
	If Not IsArray($Enum) Then
		Return SetError(1, 0, 0)
	EndIf
	Return $Enum
EndFunc   ;==>_EnumIconFile

;~ функция из GDIP.au3
Func _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight, $iStride = 0, $iPixelFormat = 0x0026200A, $pScan0 = 0)

	Local $aResult = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromScan0', 'int', $iWidth, 'int', $iHeight, 'int', $iStride, 'int', $iPixelFormat, 'ptr', $pScan0, 'ptr*', 0)

	If @error Then
		Return SetError(1, 0, 0)
	Else
		If $aResult[0] Then
			Return SetError($aResult[0], 0, 0)
		EndIf
	EndIf
	Return $aResult[6]
EndFunc   ;==>_GDIPlus_BitmapCreateFromScan0
Код для AutoIt v3.3.12.0.
Код:
#include <WinAPIShellEx.au3>
#include <WinAPIGdi.au3>
#include <WinAPIFiles.au3>
#include <WinAPI.au3>
#include <GDIPlus.au3>
#include <File.au3>

Opt('MustDeclareVars', 1)

Global $iSize = 16, $iARGB = 32; размер иконок, которые будем извлекать, и их битность (32 bit RGB + Alpha)

Global $sDirSources = @ScriptDir & '\IcoSources\', $sDirResIco = @ScriptDir & '\Ico16x16\', $sDirResPng16x16 = @ScriptDir & '\Png16x16\', _
		$sResPngBig = @ScriptDir & '\PngBig\PngBig.png', $aIcoFiles, $aTmp, $hIcon, $hBitmap, $tBits, $pBits, $hBigBitmap, $hGraphic, $sTxt

Global $iTimer = TimerInit()

;~ получаем в массив исходные файлы иконок
$aIcoFiles = _FileListToArray($sDirSources, '*.ico', 1)
If @error Then Exit 1
For $i = 1 To $aIcoFiles[0]
;~ 	получаем информацию о размерах иконок и их битность. См. описание _EnumIconFile http://autoit-script.ru/index.php?topic=10788.0
	$aTmp = _EnumIconFile($sDirSources & $aIcoFiles[$i])
	If @error Then Exit 2
;~ 	проверяем иконки на наличие размера ($iSize х $iSize) и на битность ($iARGB) для конвертации в png
	For $j = 1 To $aTmp[0][0]
		If $aTmp[$j][0] = $iSize Then
			$hIcon = 0
			If ($aTmp[$j][1] <> $iSize) Or ($aTmp[$j][2] <> $iARGB) Then Exit 3
		EndIf
	Next
	If $hIcon Then Exit 3
Next
$aTmp = 1
;~ инициируем GDI+
_GDIPlus_Startup()
Do
;~ 	Создаем прозрачный битмап с нужным размером для объединенной картинки $sResPngBig
	$hBigBitmap = _GDIPlus_BitmapCreateFromScan0($iSize * $aIcoFiles[0], $iSize)
	If @error Then ExitLoop
;~ 	Получаем его контекст
	$hGraphic = _GDIPlus_ImageGetGraphicsContext($hBigBitmap)
	If @error Then ExitLoop
	$aTmp = 0
Until 1
If $aTmp Then
;~ 	если ошибка, то удаляеем битмап и выход
	If $hBigBitmap Then _GDIPlus_ImageDispose($hBigBitmap)
	_GDIPlus_Shutdown()
	Exit 4
EndIf
$tBits = DllStructCreate('byte[' & (4 * $iSize ^ 2) & ']')
$pBits = DllStructGetPtr($tBits)
;~ текст с информацией для дальнейшего использования полученных файлов
$sTxt = 'Local $a_Country[' & $aIcoFiles[0] + 1 & '][4] = [[' & $aIcoFiles[0] & '], _' & @CRLF
For $i = 1 To $aIcoFiles[0]
;~ 	извлекаем иконку нужного размера
	$hIcon = _WinAPI_ShellExtractIcon($sDirSources & $aIcoFiles[$i], 0, $iSize, $iSize)
	If @error Then Exit 5
;~ 	сохраняем иконку в файл с новым именем (по номеру)
	_WinAPI_SaveHICONToFile($sDirResIco & $i & '.ico', $hIcon)
	If @error Then ExitLoop
;~ 	получаем информацию об иконке
	$aTmp = _WinAPI_GetIconInfo($hIcon)
	If @error Then ExitLoop
;~ 	копируем нужную информацию в структуру $tBits
	_WinAPI_GetBitmapBits($aTmp[5], 4 * $iSize ^ 2, $pBits)
	If @error Then ExitLoop
;~ 	создаем из $tBits битмап на прозрачном фоне
	$hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize, $GDIP_PXF32ARGB, 4 * $iSize, $pBits)
	If @error Then ExitLoop
	For $j = 4 To 5
;~ 		удаляеем то, что уже не нужно
		If Not _WinAPI_DeleteObject($aTmp[$j]) Then ExitLoop 2
	Next
;~ 	сохраняем битмап в файл png с новым именем (по номеру)
	If Not _GDIPlus_ImageSaveToFile($hBitmap, $sDirResPng16x16 & $i & '.png') Then ExitLoop
;~ 	рисуем битмап иконки на прозрачном битмапе (файл png $sResPngBig)
	If Not _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, ($i - 1) * $iSize, 0, $iSize, $iSize) Then ExitLoop
;~ 	удаляеем то, что уже не нужно
	If Not _GDIPlus_ImageDispose($hBitmap) Then ExitLoop
;~ 	удаляеем то, что уже не нужно
	If Not _WinAPI_DestroyIcon($hIcon) Then ExitLoop
	$aTmp = 0
	$hBitmap = 0
	$hIcon = 0
;~ 	добавляем к тексту новую строку с именем файла, удалив расширение, и его индексом
	$sTxt &= '['''', ''' & _WinAPI_PathRemoveExtension($aIcoFiles[$i]) & ''', '''', ' & $i & '], _' & @CRLF
Next
$tBits = 0
$pBits = 0
;~ если ошибка, то удаляеем все
If ($hIcon) Or ($hBitmap) Or (UBound($aTmp) = 6) Then
	$pBits = 1
	If $hIcon Then _WinAPI_DestroyIcon($hIcon)
	If $hBitmap Then _GDIPlus_ImageDispose($hBitmap)
	If UBound($aTmp) Then
		For $j = 4 To 5
			_WinAPI_DeleteObject($aTmp[$j])
		Next
	EndIf
Else
;~ 	сохраняем результат в файл png $sResPngBig
	$tBits = _GDIPlus_ImageSaveToFile($hBigBitmap, $sResPngBig)
EndIf
;~ удаляем все и освобождаем GDI+
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_ImageDispose($hBigBitmap)
_GDIPlus_Shutdown()
If $pBits Then
	MsgBox(16, 'Error', 'Error')
	Exit 13
Else
	$sTxt = StringTrimRight($sTxt, 5) & ']' & @CRLF
	ConsoleWrite($sTxt & @LF);текст можно сразу записать в au3 файл для дальнейшего использования
	MsgBox(64, 'Info', TimerDiff($iTimer) & @LF & 'Save PngBig: ' & $tBits)
EndIf

; #FUNCTION# ====================================================================================================================
; Name...........: _EnumIconFile
; Description....: Enumerates an icons from the specified .ico file.
; Syntax.........: _EnumIconFile ( $sFile )
; Parameters.....: $sFile  - The path to the .ico file whose icons are to be enumerated.
; Return values..: Success - The 2D array containing the following information:
;
;                            [0][0] - Number of rows in array (n)
;                            [0][i] - Unused
;                            [n][0] - The width of the icon, in pixels.
;                            [n][1] - The heigth of the icon, in pixels.
;                            [n][2] - The color depth of the icon, in bits-per-pixel.
;                            [n][3] - 1 (TRUE) if the icon has a PNG compression (Windows Vista+), or 0 (FALSE) otherwise.
;
;                  Failure - 0 and sets the @error flag to non-zero.
; Author.........: Yashied
; Modified.......:
; Remarks........: None
; Related........:
; Link...........: http://autoit-script.ru/index.php?topic=10788.0
; Example........:
; ===============================================================================================================================
Func _EnumIconFile($sFile)

	Local $hFile, $tEntry, $tHeader, $tData, $pData, $pIcon, $Bytes, $Count, $Offset, $Enum = 0

	$hFile = _WinAPI_CreateFileEx($sFile, 3, 0x80000000, 0x03)
	If @error Then
		Return SetError(1, 0, 0)
	EndIf
	Do
		$Bytes = _WinAPI_GetFileSizeEx($hFile)
		If Not $Bytes Then
			ExitLoop
		EndIf
		$tData = DllStructCreate('byte[' & $Bytes & ']')
		$pData = DllStructGetPtr($tData)
		If Not _WinAPI_ReadFile($hFile, $pData, $Bytes, $Bytes) Then
			ExitLoop
		EndIf
		$tHeader = DllStructCreate('ushort;ushort;ushort', $pData)
		$Count = DllStructGetData($tHeader, 3)
		If Not $Count Then
			ExitLoop
		EndIf
		Dim $Enum[$Count + 1][4] = [[$Count]]
		For $i = 1 To $Count
			$tEntry = DllStructCreate('byte;byte;byte;byte;ushort;ushort;long;long', $pData + 6 + 16 * ($i - 1))
			$Offset = DllStructGetData($tEntry, 8)
			$pIcon = $pData + $Offset
			$Enum[$i][2] = DllStructGetData($tEntry, 6)
			If DllStructGetData(DllStructCreate('byte[8]', $pIcon), 1) = Binary('0x89504E470D0A1A0A') Then
				; PNG => Retrieve IHDR chunk data (always first chunk, offset = 8)
				$tHeader = DllStructCreate('dword;dword;byte;byte;byte;byte;byte', $pIcon + 16)
				$Enum[$i][0] = _WinAPI_SwapDWord(DllStructGetData($tHeader, 1))
				$Enum[$i][1] = _WinAPI_SwapDWord(DllStructGetData($tHeader, 2))
				$Enum[$i][3] = 1
			Else
				; ICO => Retrieve BITMAPINFOHEADER structure
				$tHeader = DllStructCreate($tagBITMAPINFOHEADER, $pIcon)
				$Enum[$i][0] = DllStructGetData($tHeader, 2)
				$Enum[$i][1] = DllStructGetData($tHeader, 3) / 2
				$Enum[$i][3] = 0
			EndIf
		Next
	Until 1
	_WinAPI_CloseHandle($hFile)
	If Not IsArray($Enum) Then
		Return SetError(1, 0, 0)
	EndIf
	Return $Enum
EndFunc   ;==>_EnumIconFile
Для примера прикрепляю архив с используемыми в скрипте файлами и папками.
Без изменения путей код будет работать только со структурой папок и файлов из архива.

Если кому-нибудь интересно применение полученной объединенной картинки (пример есть в библиотеке YashiedAnimate.au3), могу показать, как использовать эту картинку в скрипте.
Продолжение темы Получение и использование частей картинок в оконных функциях.
 

Вложения

  • Convert_Ico_Png.7z
    165.2 КБ · Просмотры: 12

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Не завелась.. Код выхода 13
Версия 3.3.10.2
 
Автор
madmasles

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
inververs [?]
Не завелась.. Код выхода 13
Версия 3.3.10.2
При добавлении в GDIPlus.au3 функции _GDIPlus_BitmapCreateFromScan0 зачем-то поменяли местами $iPixelFormat и $iStride. Сейчас сделал две рабочие версии, см. первый пост.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Exit 3 лишнее.
Всегдя на нем выходит
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
madmasles [?]
могу показать, как использовать эту картинку в скрипте.

Мне интересно. Желательно сразу функцию, на входе которой PngBig.png, координаты, размер, а на выходе - вырезанное изображение
 
Верх