- Offizieller Beitrag
Nachdem das Thema in "Hilfe & Unterstützung" ausführlich behandelt wurde, und ich (wieder mal) eine tolle Hilfe erfahren durfte, möchte ich das Result auch als kleine UDF präsentieren.
Man übergibt der Funktion "_GDIPlus_ImageGetUniqueColors" das Handle zu einer GDI+ Bitmap und bekommt die Anzahl der verwendeten Farben zurück.
Weil das Ganze in purem AutoIt zu lange dauern würde, haben wir (Mars, Andy und ich) das zählen der Farben in eine Assemblerroutine ausgelagert. Diese kleine (40 Byte) Routine befindet sich als Binaercode in der UDF und wird direkt vom Speicher aus aufgerufen.
Die UDF sowie ein Beispielscript (mit Beispielbild im Binärformat) befindet sich im ZIP-Archiv im Anhang.
AutoIt
#AutoIt3Wrapper_UseX64=n ; 32Bit-Modus!
#Region ;************ Includes ************
#include-once
#include <GDIPlus.au3>
#include <Memory.au3>
#EndRegion ;************ Includes ************
#Region ASM-Code
#cs _CountUniqueColors ;
Use32 ; 32Bit Modus!
mov esi,dword[esp+4] ; Pixelstruct-Pointer holen
mov ecx,dword[esp+8] ; Anzahl der Pixel holen (Pixelcounter)
mov edi,dword[esp+12] ; Colorstruct-Pointer holen
xor eax,eax ; eax als Farbzaehler (auf null setzen)
@pixel_count: ; Anfang der Schleife fuer alle Pixel
mov ebx,[esi] ; Farbwert aus Pixelstruct holen
and ebx,0xffffff ; Alphachannel eliminieren
cmp byte[edi+ebx],0 ; Wert aus der Colorstruct = 0?
jnz @next ; wenn nicht 0, dann wurde die Farbe bereits gezaehlt, weiter -> @next
inc eax ; den Farbzaehler um eins erhoehen
mov byte[edi+ebx],1 ; den Wert in der Colorstruct auf 1 setzen
@next:
add esi,4 ; den Pixelstruct-Pointer um 4 erhoehen (naechstes DWORD)
dec ecx ; Pixelcounter um eins verringern
jnz @pixel_count ; wenn Pixelcount nocht nicht 0, dann Schleife wiederholen
ret ; eax (Farbzaehler) wird zurueckgegeben
#ce
#EndRegion ASM-Code
; $__IGUC_g_bCode entspricht dem obigen ASM-Code im Binaerformat
Global Const $__IGUC_g_bCode = '0x8B7424048B4C24088B7C240C31C08B1E81E3FFFFFF00803C1F00750540C6041F0183C6044975E7C3'
; Die Speichergroesse fuer den ASM-Code berechnen
Global Const $__IGUC_g_iMemSize = StringLen($__IGUC_g_bCode) / 2 - 1
; Achtung! Hier unbedingt virtuellen Speicher mit "_MemVirtualAlloc" anfordern, weil sonst
; (bei eingeschalteter Datenausfuehrungsverhinderung = DEP) AutoIt mit einer Fehlermeldung beendet wird.
Global Const $__IGUC_g_pMem = _MemVirtualAlloc(0, $__IGUC_g_iMemSize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
If $__IGUC_g_pMem = 0 Then Exit MsgBox(16, 'Error!', "Can't allocate virtual memory!")
; Struktur fuer den ASM-Code im virtuellen Speicher erstellen
Global $__IGUC_g_tCodeBuffer = DllStructCreate('byte[' & $__IGUC_g_iMemSize & ']', $__IGUC_g_pMem)
; den ASM-Code in den Speicher schreiben (wird unten bei DllCallAddress aufgerufen)
DllStructSetData($__IGUC_g_tCodeBuffer, 1, $__IGUC_g_bCode)
OnAutoItExitRegister('__IGUC_Exit')
_GDIPlus_Startup()
Func __IGUC_Exit()
_MemVirtualFree($__IGUC_g_pMem, $__IGUC_g_iMemSize, $MEM_DECOMMIT)
$__IGUC_g_tCodeBuffer = 0
_GDIPlus_Shutdown()
EndFunc ;==>__IGUC_Exit
Func _GDIPlus_ImageGetUniqueColors(ByRef $hImage)
Local $aDim, $tBitmapData, $tPixel, $pPixel, $tColors, $pColors, $aRet, $iError = 0
$aDim = _GDIPlus_ImageGetDimension($hImage)
$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], $GDIP_ILMREAD, $GDIP_PXF32PARGB)
If @error Then Return SetError(@error, 0, -1)
$tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; Pixelstruct (dword = 32 Bit pro Pixel)
If @error Then Return SetError(10 + @error, 0, -1)
$pPixel = DllStructGetPtr($tPixel)
$tColors = DllStructCreate('byte[' & 0xffffff + 1 & '];') ; Colorstruct (1 Byte pro Farbwert)
If @error Then Return SetError(20 + @error, 0, -1)
$pColors = DllStructGetPtr($tColors)
$aRet = DllCallAddress('uint:cdecl', DllStructGetPtr($__IGUC_g_tCodeBuffer), 'ptr', $pPixel, 'dword', $tBitmapData.Width * $tBitmapData.Height, 'ptr', $pColors)
$iError = 30 + @error
_GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
$tColors = 0
$tPixel = 0
$tBitmapData = 0
Return SetError($iError, 0, (IsArray($aRet) ? $aRet[0] : -1))
EndFunc ;==>_GDIPlus_ImageGetUniqueColors
Alles anzeigen