;==============
; Farben zählen
;==============

#cs ----------------------------------------------------------------------------
Rahmenproramm für den Test von Assemblerfunktionen
Die jeweils beabsichtigte Testphase ist in Zeile (*1) einzutragen
$phase    =1  _AssembleIt2("uint" . . .
              Der Binärcode wird erzeugt und an Ort und Stelle abgearbeitet.
              Debuggen möglich. Diese Phase dient dem Test des Assemblerprogramms
          =2  _AssembleIt2("retbinary" . . .
              Nach abgeschlossenem Test wird nunmehr der fertige Binärcode
              erzeugt. Er erscheint im der Datei binarycode.txt.
              Anschließend wird das Programm mittels der Funktion
              DllCallAddress("uint:cdecl" ausgeführt.
          =3  Der in Phase 2 erzeugte Binärcode wird der Datei binarycode.txt
              entnommen und mittels der Funktion DllCallAddress("uint:cdecl"
              ausgeführt.
          =0  oder ="" Der in der Datei binarycode.txt stehende Binärcode wurde direkt
              in das Programm als Variable $binarycode eingetragen und
              das Programm kann dann auch compiliert werden.
#ce ----------------------------------------------------------------------------
; ===================================================================
; Bearbeitungsphase festlegen
$phase=1    ; (*1)
$sFile=""
; ===================================================================
#include <GDIPlus.au3>
#include <assembleit2_64.au3>;~
#AutoIt3Wrapper_UseX64=n
#cs _countpixelcolors           ;
    Use32                       ;32Bit!


    org $PTR_SOURCE_ASMCODE     ;only needed for assembleit debugger


    mov edi,dword[esp+4]        ;pointer bitmap
    mov ecx,dword[esp+8]        ;width bitmap
    mov ebx,dword[esp+12]       ;height bitmap
    mov esi,dword[esp+16]       ;pointer pixelstruct
    movd xmm1,dword[esp+20]     ;pointer textstruct

    rdtsc                       ;timer sichern
    push eax

_asmdbg_()  ;debugger


    mov eax,ebx                 ;h
    mul ecx                     ;w*h
    mov edx,eax                 ;w*h
    movd xmm0, edx              ;sichern anzahl


    ;zuerst werden die Pixel durchlaufen, die Farbe jedes Pixels wird an der Speicherstelle seines "Farbwerts" hochgezählt


    @pixel_count:               ;alle pixel
    mov eax,[edi]               ;farbe
    and eax,0xFFFFFF            ;eliminieren alpha-channel
    ;~          _asmdbg_()
    inc dword [esi+eax*4]       ;farbwert=farbwert+1, address=colorRGB^^
    ;add dword [esi+eax*4],1         ;schneller?
    add edi,4                   ;nächste farbe
    sub edx,1                   ;pixelcounter
    jnz near @pixel_count       ;jump if not zero(<0)

    ;***********************************************************************************
    ;jetzt werden die gezählten Farbwerte nacheinander als Text in HEX-Darstellung und der Anzahl als Integerwert in eine textstruct geschrieben
    ;Format: 6BF1E5,1234567@crlf


    movd xmm4,edi               ;sichern pointer bitmap


    mov edx,0x1000000           ;anzahl farben 0xFFFFFF +1
    movd ebx,xmm1               ;pointer textstruct
    mov edi,-4                  ;pointer colorstruct

    @count_colors:
    sub edx,0
    jz @end
    sub edx,1                   ;jedes pixel
 ;   jz @end                     ;alle pixel durchlaufen (schwarze Pixel wurden nicht erkannt)
    mov ecx,[esi+edx*4]         ;ecx=anzahl, edx=farbwert; esi=pointer pixelstruct
    cmp ecx,0
    je @count_colors            ;wenn ungleich null...ist farbe gefunden


    ;~     _asmdbg_()

    ;hex-werte farbe =6 Bytes
    ;aus den 6 nibbles die 6 bytes machen, nibble+48 (0x30) =ASCII-Ziffer
    ;zuerst aus den beiden bytes ( Bit 4444333322221111) die nibble erweitern zu 0000444400003333 und 0000222200001111


    movd xmm3,eax               ;sichern wg registerpressure
    movd xmm4,edx               ;aka push/pop
    movd xmm5,esi
    movd xmm6,ecx


    mov eax,edx
    mov edi,eax


    ;schneller ist natürlich eine LUT^^, aber so geht es auch
    ;**************************************************************
    mov edx,0                   ;6  nibbles
    _int2hex:
    mov ecx,20                  ;5*4 shiften
    lea esi,[4*edx]             ;anzahl der zu shiftenden bits
    sub ecx,esi                 ;ecx=20,ecx
    shr eax,cl                  ;nach al shiften
    and al,0xF                  ;obere 4 nibble eliminieren
    cmp al,9                    ;größer oder kleiner als A?
    jle _groesserA
    add al,7                    ;A-F
    _groesserA:
    add al,48                   ;1-9

    mov byte[ebx+edx],al        ;ascii-hexcode in struct
    mov eax,edi

    add edx,1
    ;    _asmdbg_()
    cmp edx,6                   ;alle 6 nibble bearbeitet?
    jne _int2hex

    add ebx,edx                 ;ein zeichen (6 bytes) weiter
    mov byte [ebx],44           ;komma
    add ebx,1                   ;ein zeichen weiter

    mov edi,ebx                 ;pointer text
    movd ebx,xmm6               ;anzahl integer

    ;************************************************************
    ;aus einer Zahl(Registerinhalt) einen ZiffernString machen: http://dcla.rkhb.de/umwandlung/int2dez.html
    ;und in die struct schreiben
    push   ebx                  ;alle benötigten Register sichern
    push   ecx                  ;alle benötigten Register sichern
    mov eax, ebx                ;Zahl laden
    mov ebx, 10                 ;Divisor
    xor ecx, ecx                ;ECX=0 (Anzahl der Ziffern)
    Schleife_1:
    xor edx, edx
    div ebx                     ; EDX:EAX / EBX = EAX Rest EDX
    push dx                     ; LIFO
    add cl,1                    ; ADD soll schneller sein als INC
    or  eax, eax                ; AX = 0?
    jnz Schleife_1              ; nein: nochmal
    Schleife_2:
    pop ax                      ; gepushte Ziffern zurückholen
    or al, 00110000b            ; Umwandlung in ASCII
    stosb                       ; Nur AL nach [EDI] (EDI ist ein Zeiger auf den String)
    loop Schleife_2             ; bis keine Ziffern mehr da sind
    mov byte [edi],0Dh          ;CR  CarriageReturn, man könnte auch ein Komma (ascii=2C) einsetzen, dazu noch ein nullbyte als EndOfString
    add edi,1                   ;ein Byte weiter
    pop   ecx                   ;Register wiederherstellen
    pop   ebx                   ;Register wiederherstellen
    ;************************************************************Ende Ziffer aus Register

    movd esi,xmm5
    movd edx,xmm4               ;restaurieren
    mov ebx,edi

	cmp edx,0
    jne @count_colors           ;schleife alle farben

    @end:
	pop    ebx
	rdtsc
	sub    eax,ebx

    ret


#ce
; ================================================================
_GDIPlus_Startup() ; Initialisiert (startet) Microsoft Windows GDI+
Global $ptr_bitmap, $hbmp, $iwidth, $iheight ;werden ByRef von _CreateNewBmp32FromFile ausgefüllt
; -------------------------------------
; Bitmapdatei zergliedert zur Verfügung stellen
If $sFile = "" Then
    $sFile = FileOpenDialog("Bilder", @ScriptDir, "Bilder (*.jpg;*.bmp;*.png)")
EndIf
$DC_bitmap = _CreateNewBmp32FromFile($sFile, $ptr_bitmap, $hbmp, $iwidth, $iheight)
                                     ; siehe D:\AU\Assembler\#FuncASS.au3
; -------------------------------------
; Die $colorstruct bereitstellen
$colorstruct = DllStructCreate("uint[" & 256 ^ 3 & "]");anzahl möglicher Farben
$ptr_colorstruct = DllStructGetPtr($colorstruct)
; -------------------------------------
; Die $textstruct bereitstellen.
$textstruct = DllStructCreate("char[" & 18 * $iwidth * $iheight & "]");maximale Anzahl Zeichen
$ptr_textstruct = DllStructGetPtr($textstruct)
;9 Stellen integer
;6 Stellen Hexdarstellung
;1 Stellen Komma
;2 Stellen CRLF

; -------------------------------------
; Bearbeitung durchführen
Select
    Case $phase=1
        Local $ret = _AssembleIt2("uint", "_countpixelcolors", "ptr",  $ptr_bitmap, _
            "int_ptr", $iwidth, "int_ptr", $iheight, "int_ptr", $ptr_colorstruct, "int_ptr", $ptr_textstruct)
        Auswerten($textstruct)
    Case $phase=0
		$binarycode="0x8B7C24048B4C24088B5C240C8B742410660F6E4C24140F315089D8F7E189C2660F6EC28B0725FFFFFF00FF048683C70483EA010F85EAFFFFFF660F6EE7BA00000001660F7ECBBFFCFFFFFF83EA000F849300000083EA018B0C9683F90074EC660F6ED8660F6EE2660F6EEE660F6EF189D089C7BA00000000B9140000008D34950000000029F1D3E8240F3C097E020407043088041389F883C20183FA0675D901D3C6032C83C30189DF660F7EF3535189D8BB0A00000031C931D2F7F3665280C10109C075F366580C30AAE2F9C6070D83C701595B660F7EEE660F7EE289FB83FA000F8564FFFFFF5B0F3129D8C3"
 		Ausfuehren($binarycode)
    Case $phase=2
        asmdbg(@ScriptLineNumber)  ; Direktiven _asmdbg_() mit ";" versehen
        $binarycode = _AssembleIt2("retbinary", "_countpixelcolors")
       ; Binären Assemblercode als Datei ausgeben:
        $h=FileOpen("binarycode.txt",2)
        FileWrite($h,StringMid($binarycode,3)) ; Ohne 0x
        FileClose($h)
		Ausfuehren($binarycode)
    Case $phase=3
		$h=FileOpen("binarycode.txt")
		$binarycode="0x"&FileRead($h)
		FileClose($h)
		Ausfuehren($binarycode)
EndSelect
MsgBox(0,"","Fertig",1)
Exit
;======================================================================
#cs ----------------------------------------------------------------------------
Erhält den durch die Assemblierung erzeugten Maschinencode $binarycode
und arbeitet diesen ab.
Als globale Variable müssen existiern:
- $ptr_bitmap       Zeiger auf die Structur, welche die Bitmap-Daten enthält
- $iwidth           Zeiger auf die Breite der Bitmap (Anzahl der Pixel-Spalten)
- $iheight          Zeiger auf die Höhe der Bitmap (Anzahl der Pixel-Zeilen
- $ptr_colorstruct  Zeiger auf Arbeitfeld (Structur) mit der Anzahl von
                    256^3 32-Bit-Integers, in welchem das Ass-Programm
					die Farben zählt
- $ptr_textstruct   Zeiger auf eine Structur, in welcher das Ass-Programm
                    das Ergebnis hinterlässt
#ce ----------------------------------------------------------------------------
Func Ausfuehren($binarycode)
$tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
DllStructSetData($tCodeBuffer, 1, $binarycode)
$t = TimerInit()
; -----------
DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), _
               "ptr", $ptr_bitmap, "int_ptr", $iwidth, "int_ptr", $iheight, _
			  "int_ptr", $ptr_colorstruct, "int_ptr", $ptr_textstruct)
; -----------
$m = TimerDiff($t)
; in der Structur $textstruct stehen nun die gefundenen Farben
; in der Form RRGGBB,Anzahl@LF
Opt("WinTitleMatchMode", 2)     ; 1=vom Anfang, 2=enthält, 3=genau, 4=fortgeschritten, -1 bis -4=Groß-/Klein ignorieren
WinSetState("SciTE","",@SW_MINIMIZE)
MsgBox(0x40000, "Timer ASM pixelcounter", "Within " & Int($m) & "ms " )
Auswerten($textstruct)
EndFunc
;======================================================================
#cs ----------------------------------------------------------------------------
Wertet das Ergebnis des Assemblerprogramms aus
#ce ----------------------------------------------------------------------------
Func Auswerten($textstruct)
$text = DllStructGetData($textstruct, 1)
$text = StringTrimRight($text, 1) ;letztes LF entfernen
$text = StringReplace($text, @CR, @CRLF) ;autoit is fast too^^
$number_colors = @extended + 1
$colorarray = StringSplit($text, @LF)
Opt("WinTitleMatchMode", 2)     ; 1=vom Anfang, 2=enthält, 3=genau, 4=fortgeschritten, -1 bis -4=Groß-/Klein ignorieren
WinSetState("SciTE","",@SW_MINIMIZE)
Sleep(500)
_ArrayDisplay($colorarray,"$colorarray")
EndFunc
;======================================================================
#cs ----------------------------------------------------------------------------
Zergliedert eine Bitmap-Datei
$bmpfile   Pfad-/Dateiname der zue zergliedernden Datei

Hinterlässt:
$ptr       Ponter auf das Pixelfeld der Datei. 4 Bytes je Pixel.
           Beginnend mit dem linken oberen Pixel, zeielweise bis zum rechten unteren Pixel.
$hbmp      Siehe: https://learn.microsoft.com/de-de/windows/win32/gdi/device-independent-bitmaps
$iwidth    Breite der Bitmap, d.h. Anzahl Pixel pro Zeile
$iheight   Höhe der Bitmap, d.h. Anzahl der Zeilen
#ce ----------------------------------------------------------------------------
#include <GDIPlus.au3>
Func _CreateNewBmp32FromFile($bmpfile, ByRef $ptr, ByRef $hbmp, ByRef $iwidth, ByRef $iheight) ;ptr to bitmapdata, it is possible to manipulate one pixel if needed

Local $hbitmap, $hdc, $hcdc

$hbitmap = _GDIPlus_ImageLoadFromFile($bmpfile)
If @error Or $hbitmap = 0 Then
    MsgBox(0, "Func _CreateNewBmp32FromFile()", "Error opening File: " & @CRLF & $bmpfile)

    Return -1
EndIf
$hbmpfile = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hbitmap)
$iwidth = _GDIPlus_ImageGetWidth($hbitmap)
$iheight = _GDIPlus_ImageGetHeight($hbitmap)
$hcdc = _WinAPI_CreateCompatibleDC(0)
_WinAPI_SelectObject($hcdc, $hbmpfile) ;image im hcdc

    ;neue bitmap
$tBMI = DllStructCreate($tagBITMAPINFO) ;Struktur der Bitmapinfo erstellen und Daten eintragen
DllStructSetData($tBMI, 1, DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette
DllStructSetData($tBMI, 2, $iwidth)
DllStructSetData($tBMI, 3, -$iheight) ;minus =standard = bottomup
DllStructSetData($tBMI, 4, 1)
DllStructSetData($tBMI, 5, 32) ;32 Bit = 4 Bytes => AABBGGRR
$adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0)
$hbmp = $adib[0]            ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden
$ptr = $adib[4]             ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet

$hdc = _WinAPI_CreateCompatibleDC(0)
_WinAPI_SelectObject($hdc, $hbmp) ;leere bitmap im hdc
_WinAPI_BitBlt($hdc, 0, 0, $iwidth, $iheight, $hcdc, 0, 0, $srccopy);image in leere bitmap
_WinAPI_DeleteDC($hcdc)
_WinAPI_DeleteObject($hbmpfile)

Return $hdc                 ;DC der Bitmap zurückgeben

EndFunc                         ;==>_CreateNewBmp32FromFile
;======================================================================
#cs ----------------------------------------------------------------------------
Sucht den letzten Abschnitt mit Assemblercodse  #cs .... #ce  vor diesem
Funktionsaufruf auf und stellt im Assemblercode den Direktiven _asmdbg_()
ein Semikolon voran.
Als $number ist @ScriptLineNumber einzutragen. Das heißt, der Aufruf muss
immer so aussehen:
asmdbg(@ScriptLineNumber)

#ce ----------------------------------------------------------------------------
Func asmdbg($number)

$script=@ScriptFullPath
; -------------------------------------
; Scrip-Datei in Array $linearr einlesen
$lineanz=0
Local $linearr[$lineanz]
$h=FileOpen($script)
While 1
    $line=FileReadLine($h)
    If @error=-1 Then ExitLoop ; Dateiende
    ReDim $linearr[$lineanz+1]
    $linearr[$lineanz]=$line
    $lineanz+=1
WEnd
FileClose($h)
;_ArrayDisplay($linearr)
; -------------------------------------
; Assemblerabschnitt suchen
$csx=-1
$cex=-1
$ass=0  ; d.h. außerhalb Kommentarabschnitt
For $i=0 To $number
    Select
        Case StringInStr($linearr[$i],"#cs")
			If $ass=1 Then  Fehler("Zwei #cs folgen aufeinander - Zeile "&$i)
			;If $cex<>-1  Then  Fehler("Zwei #cs folgen aufeinander - Zeile "&$i
			$csx=$i
			$ass=1  ; d.h. innerhalb Kommentarabschnitt
        Case StringInStr($linearr[$i],"#ce")
			If $ass=0 Then  Fehler("Vor #ce kein #cs gefunden - Zeile "&$i)
            $cex=$i
            $ass=0  ; d.h.  Assemblerabschnitt beendet
    EndSelect
Next
If $csx=-1 OR $cex=-1  Then Fehler("Keinen Assemblercode gefunden - Zeile "&$i)
If $cex<$csx Then Fehler("Kommentarabschnitt endet nicht - Zeile "&$csx)
; -------------------------------------
; Array $linearr in Hilsdatei ausgeben und dabei die _asmdbg_() mit ";" versehen
$hh=FileOpen("hilf.au3",2)
For $i=0 To $lineanz-1
    If $i>$csx AND $i<$cex Then
        $pos=StringInStr($linearr[$i],"_asmdbg_()")
        If $pos Then ; _asmdbg_() gefunden
            $po=StringInStr($linearr[$i],";",0,1,1,$pos)
            If $po=0 Then  ; es gibt noch kein ";"
                If Stringleft($linearr[$i],1)<>" " Then
                    $linearr[$i]=";"&$linearr[$i]
                Else
                    $linearr[$i]=";"&StringMid($linearr[$i],2)
                EndIf
            EndIf
        EndIf
    EndIf
    FileWriteLine($hh,$linearr[$i])
Next
FileClose($hh)
FileCopy("hilf.au3",$script,1)
$sciteExe="C:\Program Files (x86)\AutoIt3\SciTE\SciTE.exe"
Run($sciteExe&" "&$script)
Opt("WinTitleMatchMode", 2)     ; 1=vom Anfang, 2=enthält, 3=genau, 4=fortgeschritten, -1 bis -4=Groß
While 1
    Sleep(100)
    WinActivate(@ScriptName)
    If WinActive(@ScriptName) Then Exitloop
WEnd

EndFunc
;======================================================================
#cs ----------------------------------------------------------------------------
#ce ----------------------------------------------------------------------------
Func Fehler($txt)
MsgBox(0,"",$txt)
Exit
EndFunc
