Scrolling+Screenshot

  • Hallo,
    ich brauchte mal ein Programm, um einen "langen" Screenshot zu machen. Also einfach gesagt, um zum Beispiel eine ganze Webseite zu "screenshotten". Man kann einen Bereich auswählen, von dem 4 mal pro Sekunde ein Screenshot gemacht wird, sofern sich die Checksum geändert hat. (Eine Nutzung der Checksum ohne Bothintergrund :P) Am Ende werden die Screenshots dann zusammengesetzt.
    Ich habe das ganze programmiert, bevor ich nach sowas gegooglet hab und wollte eigentlich nur schnell meinen Nutzen daraus ziehen, daher ist es leider nicht total komfortabel, aber es funktioniert. Ich hab es auch nur mit Autoit programmiert, keine ASM-Unterstützung ^^
    Wer "Toleranz" braucht, wegen bewegenden Elemten, der sollte im Code gucken, da steht wo man es machen kann. Allerdings dauert das Verfahren dann wesentlich länger und ist nicht mehr so treffsicher ..

    Also, hier der Code:

    Spoiler anzeigen
    [autoit]

    #NoTrayIcon
    #Region ;**** Directives created by AutoIt3Wrapper_GUI ****
    #AutoIt3Wrapper_Icon=TheShadowAE.ico
    #AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
    #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>
    #include <Math.au3>
    #include <Misc.au3>
    #include <GUIT.au3>
    #include <ScreenCapture.au3>
    #include "inlineASM.au3"

    [/autoit] [autoit][/autoit] [autoit]

    _ASMinit()
    $asm_func=_ASMcreateFunc("_ASMcode")
    ConsoleWrite("$asm_func="&$asm_func&@CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    MsgBox(0,"Information","How you make a screenshot:"&@CRLF&"1. go to the right window and close this message"&@CRLF&"2. mark the area"&@CRLF&"3. scroll (down)"&@CRLF&"4. press enter")

    [/autoit] [autoit][/autoit] [autoit]

    $gui=_CreateGUIT("BlockMark",@DesktopWidth,@DesktopHeight,0,BitOR($WS_EX_TOOLWINDOW,$WS_EX_TOPMOST))
    WinMove($gui[0],"",0,0)
    GUISetState(@SW_SHOW,$gui[0])

    [/autoit] [autoit][/autoit] [autoit]

    $contloop=True
    Local $del=0 ;for no error
    _GDIPlus_Startup()

    [/autoit] [autoit][/autoit] [autoit]

    _GDIPlus_GraphicsClear($gui[5],0x01000000)
    _DrawonGUIT($gui)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    While $contloop
    Local $coords[4]=[-1,-1,-1,-1]

    [/autoit] [autoit][/autoit] [autoit]

    Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
    $contloop=False
    EndSwitch

    [/autoit] [autoit][/autoit] [autoit]

    If _IsPressed("1") Then
    $pos=MouseGetPos()
    $coords[0]=$pos[0]
    $coords[1]=$pos[1]

    [/autoit] [autoit][/autoit] [autoit]

    While _IsPressed("1")
    _GDIPlus_GraphicsClear($gui[5],0x01000000)
    $pos=MouseGetPos()
    Local $rect[4]=[$coords[0],$coords[1],$pos[0],$pos[1]]
    _adjust($rect)
    _GDIPlus_GraphicsDrawRect($gui[5],$rect[0],$rect[1],$rect[2]-$rect[0],$rect[3]-$rect[1])
    _DrawonGUIT($gui)
    Sleep(10)
    WEnd

    [/autoit] [autoit][/autoit] [autoit]

    $pos=MouseGetPos()
    $coords[2]=$pos[0]
    $coords[3]=$pos[1]
    _adjust($coords)
    _GDIPlus_GraphicsClear($gui[5],0x00000000)
    _GDIPlus_GraphicsDrawRect($gui[5],$coords[0]-1,$coords[1]-1,$coords[2]-$coords[0]+2,$coords[3]-$coords[1]+2)
    _DrawonGUIT($gui)

    [/autoit] [autoit][/autoit] [autoit]

    $timer=TimerInit()
    $diff=TimerDiff($timer)-250
    $i=0
    $pixelsum=0

    [/autoit] [autoit][/autoit] [autoit]

    While Not _IsPressed("0D")
    If TimerDiff($timer)>$diff+250 Then
    $diff=TimerDiff($timer)
    $pxsum=PixelChecksum($coords[0],$coords[1],$coords[2],$coords[3],2)
    If $pixelsum=0 Or $pixelsum<>$pxsum Then
    $pixelsum=$pxsum
    $file=@TempDir&"\scrollscreen_"&$i&".png"
    FileDelete($file)
    _ScreenCapture_Capture($file,$coords[0],$coords[1],$coords[2],$coords[3],False)
    $i+=1
    EndIf
    EndIf
    Sleep(10)
    WEnd
    GUISetState(@SW_HIDE,$gui[0])

    [/autoit] [autoit][/autoit] [autoit]

    ;Bilder zusammenfügen
    ProgressOn("In Progress..","cutting screenshots togehter","1/"&($i),Default,Default,16) ;+2 = not on top
    ConsoleWrite("Start!"&@CRLF&@CRLF)
    $completeImage=_GDIPlus_BitmapCreateFromFile(@TempDir&"\scrollscreen_0.png")
    ConsoleWrite("$completeImage="&$completeImage&@CRLF)
    $w=$coords[2]-$coords[0]+1
    $h=$coords[3]-$coords[1]+1
    ConsoleWrite("$w="&$w&@CRLF)
    ConsoleWrite("$h="&$h&@CRLF)
    $comph=$h
    For $x=1 To $i-1
    ProgressSet(($x+1)/$i*100,($x+1)&"/"&$i)
    ConsoleWrite("Progress: "&($x+1)&"/"&$i&@CRLF)
    Local $nextIMG=_GDIPlus_BitmapCreateFromFile(@TempDir&"\scrollscreen_"&$x&".png")
    ConsoleWrite("$nextIMG="&$nextIMG&@CRLF)
    Local $lock1=_GDIPlus_BitmapLockBits($completeImage,0,$comph-$h,$w,$h,$GDIP_ILMREAD,$GDIP_PXF32ARGB)
    ConsoleWrite("$lock1="&IsDllStruct($lock1)&", @error is "&@error&@CRLF)
    ConsoleWrite("Scan0: "&DllStructGetData($lock1,"Scan0")&@CRLF)
    Local $lock2=_GDIPlus_BitmapLockBits($nextIMG,0,0,$w,$h,$GDIP_ILMREAD,$GDIP_PXF32ARGB)
    ConsoleWrite("$lock2="&IsDllStruct($lock2)&", @error is "&@error&@CRLF)
    ConsoleWrite("Scan0: "&DllStructGetData($lock2,"Scan0")&@CRLF)
    Local $s1=DllStructCreate("dword["&($w*$h)&"]",DllStructGetData($lock1,"Scan0"))
    Local $s2=DllStructCreate("dword["&($w*$h)&"]",DllStructGetData($lock2,"Scan0"))
    Local $concaty=_GetImageInImageY_ASM($w,$h,$s1,$s2)
    _GDIPlus_BitmapUnlockBits($completeImage,$lock1)
    _GDIPlus_BitmapUnlockBits($nextIMG,$lock2)
    If $concaty>=1 Then
    $newIMG=_GDIPlus_BitmapCreateFromGraphics($w,$comph+$concaty,$gui[5])
    ConsoleWrite("$newIMG="&$newIMG&@CRLF)
    ConsoleWrite("Height: "&_GDIPlus_ImageGetHeight($newIMG)&@CRLF)
    $gra=_GDIPlus_ImageGetGraphicsContext($newIMG)
    ConsoleWrite("$gra="&$gra&@CRLF)
    _GDIPlus_GraphicsClear($gra) ;for safety
    _GDIPlus_GraphicsDrawImageRect($gra,$completeImage,0,0,$w,$comph)
    _GDIPlus_GraphicsDrawImageRect($gra,$nextIMG,0,$comph-$h+$concaty,$w,$h)
    _GDIPlus_GraphicsDispose($gra)
    $comph+=$concaty
    _GDIPlus_BitmapDispose($completeImage)
    $completeImage=$newIMG
    ConsoleWrite("$completeImage="&$completeImage&@CRLF)
    EndIf
    _GDIPlus_BitmapDispose($nextIMG)
    $del=FileDelete(@TempDir&"\scrollscreen_"&$x&".png")
    If $del Then ConsoleWrite("Deleted: "&"scrollscreen_"&$x&".png"&@CRLF)
    ConsoleWrite(@CRLF)
    Next
    ProgressOff()
    $del=FileDelete(@TempDir&"\scrollscreen_0.png")
    If $del Then ConsoleWrite("Deleted: "&"scrollscreen_0.png"&@CRLF)
    ConsoleWrite("Finished!"&@CRLF)

    [/autoit] [autoit][/autoit] [autoit]

    ;Speichern
    $path=FileSaveDialog("Save the Screenshot","","PNG (*.png)|Images (*.png;*.jpg;*.jpeg;*.bmp)|All (*.*)",18,"screenshot.png")
    ConsoleWrite("Path: "&$path&"; @error="&@error&@CRLF)
    If Not @error Then _GDIPlus_ImageSaveToFile($completeImage,$path)
    _GDIPlus_BitmapDispose($completeImage)

    [/autoit] [autoit][/autoit] [autoit]

    $contloop=False
    ;~ GUISetState(@SW_SHOW,$gui[0])
    EndIf

    [/autoit] [autoit][/autoit] [autoit]

    Sleep(10)
    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _DeleteGUIT($gui)
    _GDIPlus_Shutdown()
    Exit

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _GetImageInImageY($w,$h,$s1,$s2)
    ConsoleWrite("---_GetImageInImageY---"&@CRLF)
    Local $line=-1
    For $i=0 To $h-1
    Local $trifft=0, $min=Ceiling(($h-$i)*$w*1) ;*0.8=tolerance factor (1=no tolerance) (for smiley movement etc.)
    For $y=$i To $h-1
    For $x=0 To $w-1
    Local $p1,$p2
    $p1=DllStructGetData($s1,1,$y*$w+$x+1)
    $p2=DllStructGetData($s2,1,($y-$i)*$w+$x+1)
    If $p1=$p2 Then
    $trifft+=1
    If $trifft=$min Then ExitLoop 2
    Else
    Local $pixleft=($h-$y)*$w-$x-1
    If $pixleft<($min-$trifft) Then ExitLoop 2
    EndIf
    Next
    Next
    ;~ ConsoleWrite("$i="&$i&"; "&$trifft&"/"&$min&@CRLF)
    If $trifft=$min Then
    $line=$i
    ExitLoop
    EndIf
    Next
    ConsoleWrite("Return: "&$line&@CRLF)
    ConsoleWrite("-----------------------"&@CRLF)
    Return $line
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _GetImageInImageY_ASM($w,$h,$s1,$s2)
    ConsoleWrite("---_GetImageInImageY_ASM---"&@CRLF)
    Local $p1=DllStructGetPtr($s1),$p2=DllStructGetPtr($s2)
    Local $ret=DllCallAddress("int",$asm_func,"int",$w,"int",$h,"ptr",$p1,"ptr",$p2)
    ConsoleWrite("Return: "&$ret[0]&@CRLF)
    ConsoleWrite("---------------------------"&@CRLF)
    Return $ret[0]
    EndFunc

    [/autoit] [autoit][/autoit] [autoit]

    Func _ASMcode()
    _("finit")
    _("mov ebx,[esp+4]") ;w
    _("mov edx,[esp+8]") ;h
    _("mov esi,[esp+12]") ;p1
    _("mov edi,[esp+16]") ;p2

    [/autoit] [autoit][/autoit] [autoit]

    _("xor eax,eax") ;
    _("loop_i:") ;
    _(" movd xmm0,eax ;i") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" mov ecx,edx") ;
    _(" sub ecx,eax") ;
    _(" imul ecx,ebx") ;
    ;hier toleranz (ecx*0.8)
    _(" movd xmm1,ecx") ;min

    [/autoit] [autoit][/autoit] [autoit]

    _(" xor eax,eax") ;
    _(" movd xmm2,eax") ;trifft
    _(" movdqa xmm3,xmm0") ;y=i
    _(" loop_y:") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" xor ecx,ecx") ;
    _(" loop_x:") ;
    _(" movd xmm4,ecx") ;x

    [/autoit] [autoit][/autoit] [autoit]

    _(" lodsd") ;
    _(" cmp eax,[edi]") ;
    _(" jne @f") ;
    ;pixel=pixel
    _(" movd eax,xmm2") ;
    _(" inc eax") ;
    _(" movd xmm2,eax") ;
    _(" movd ecx,xmm1") ;
    _(" cmp eax,ecx") ;
    _(" je _return") ;
    _(" jmp .weiter") ;
    _(" @@:") ;pixel!=pixel
    _(" mov eax,edx") ;
    _(" movd ecx,xmm3") ;
    _(" sub eax,ecx") ;
    _(" imul eax,ebx") ;
    _(" movd ecx,xmm4") ;
    _(" sub eax,ecx") ;
    _(" dec eax") ;
    _(" movd xmm7,eax") ;temp
    _(" movd ecx,xmm1") ;
    _(" movd eax,xmm2") ;
    _(" sub ecx,eax") ;
    _(" movd eax,xmm7") ;
    _(" cmp eax,ecx") ;
    _(" jb _next") ;
    _(" .weiter:") ;
    _(" add edi,4") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" movd ecx,xmm4") ;
    _(" inc ecx") ;
    _(" cmp ecx,ebx") ;
    _(" jb loop_x") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" movd ecx,xmm3") ;
    _(" inc ecx") ;
    _(" movd xmm3,ecx") ;
    _(" cmp ecx,edx") ;
    _(" jb loop_y") ;
    _(" _next:") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" movd eax,xmm0") ;
    _(" inc eax") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" mov esi,[esp+12]") ;
    _(" mov edi,[esp+16]") ;
    _(" mov ecx,eax") ;
    _(" imul ecx,ebx") ;
    _(" shl ecx,2") ;
    _(" add esi,ecx") ;

    [/autoit] [autoit][/autoit] [autoit]

    _(" cmp eax,edx") ;
    _("jb loop_i") ;

    [/autoit] [autoit][/autoit] [autoit]

    _("_return:") ;
    _("movd eax,xmm0") ;
    _("movd ecx,xmm1") ;
    _("movd ebx,xmm2") ;
    _("cmp ebx,ecx") ;
    _("jne @f") ;
    _("ret 16") ;
    _("@@:") ;
    _("mov eax,-1") ;
    _("ret 16") ;
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _adjust(ByRef $rect)
    Local $new[4]=[_Min($rect[0],$rect[2]),_Min($rect[1],$rect[3]),_Max($rect[0],$rect[2]),_Max($rect[1],$rect[3])]
    $rect=$new
    Return $rect
    EndFunc

    [/autoit]

    So, im Anhang ein Beispiel für ein Erzeugnis und die Zip, die Code, Exe und Icon beinhaltet.

    Hoffentlich nützt es jemandem :)

    mfg


    EDIT:
    Neue Version jetzt doch ASM-powered. Ist alles angenehm schnell jetzt :)

  • Sehr Performant ist es wirklich nicht, aber sehr zweckmäßig.
    Wie oft habe ich schon einzelne Screenshots von Hand zusammengebastelt und damit meine Zeit verschwendet ?
    Funktioniert bei mir schonmal einwandfrei.

    lg
    M

  • Hat mit der Hilfe Datei gefunzt, aber arbeitet sehr gemütlich.

    Vollautomatisch wäre, wie gesagt, die perfekte Lösung.

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Hat mich doch sehr gerezeit das ganze doch noch mit ASM zu powern bevor ich die Woche weg bin :D Das war ja schon sehr lahm. Jetzt ist das sehr angenehm schnell :)
    Für das ganze hab ich eine umgewandelete Form von Andys assembleit benutzt, damit nur einmal assembliert werden muss (bzw. 2 mal, aber nur am Anfang).
    Der super Debugger von Andy ist trotzdem sehr hilfreich gewesen :thumbup:

  • Hi, das hatte ich vor einiger Zeit schonmal umgesetzt, um Buttons auf einer sich verändernden Website anzuklicken. Übrigens sauschnell auch per AutoIt^^, mit asm wars dann unwesentlich schneller...

    Selbst animierte Buttons werden gefunden.
    PushTheButton


    Zitat

    ASMinit()
    $asm_func=_ASMcreateFunc("_ASMcode")
    ConsoleWrite("$asm_func="&$asm_func&@CRLF)

    habe ich irgendetwas spezielles in deinem Code übersehen, oder wieso benutzt du nicht einfach

    [autoit]

    $_assembleit_flag = 0
    $ret=_assembleit("int","_ASMcode","int",$w,"int",$h,"ptr",$p1,"ptr",$p2)

    [/autoit]


    das setzen des Flags vor dem Aufruf von Assembleit führt zur vollautomatischen Erstellung von folgendem Code, der dann einfach nur per ctrl+v eingefügt werden muss...

    [autoit]

    Global $tCodeBuffer = DllStructCreate("byte[199]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1,"0x9BDBE38B5C24048B5424088B74240C8B7C241031C0660F6EC089D129C10FAFCB660F6EC931C0660F6ED0660F6FD831C9660F6EE1AD3B077513660F7ED040660F6ED0660F7EC939C87462EB2889D0660F7ED929C80FAFC3660F7EE129C848660F6EF8660F7EC9660F7ED029C1660F7EF839C8721983C704660F7EE14139D972B0660F7ED941660F6ED939D172A1660F7EC0408B74240C8B7C241089C10FAFCBC1E10201CE39D00F8269FFFFFF660F7EC0660F7EC9660F7ED339CB7503C21000B8FFFFFFFFC21000") ;write opcodes into memory
    $ret=DllCalladdress("int",DllStructGetPtr($tCodeBuffer),"int",$w,"int",$h,"ptr",$p1,"ptr",$p2)

    [/autoit]

    Damit braucht man dann weder Assembleit, noch FASM!

    Btw, die Verwendung von AssembleIt() hat den großen Vorteil, dass man sich in keinem Fall um diese RET_Geschichte kümmern muss...

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    3 Mal editiert, zuletzt von Andy (12. Oktober 2013 um 11:58)

  • Wenn man :cdecl nimmt muss man sich auch nicht um ret kümmern. An den Flag mit =0 hab ich nicht gedacht und zum Testen kann ich auch nicht jedes mal den neuen Code da rein kopieren. So hab ich auch beim Testen immer nur einmal den Kompiliervorgang.

  • Hi,
    "kompilieren" ;) musst du ohnehin...
    Der Witz ist aber doch, mittels AssembleIt() incl. Debugger den Code zu erstellen und auszutesten.
    Wenn dann der Code einwandfrei funktioniert, DANN erst erstellt man mittels des

    [autoit]

    $_Assembleit_Flag = 0

    [/autoit]

    vollautomatisch den Binärcode...und kann völlig auf den asm-sourcecode und Assembleit verzichten...

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    Einmal editiert, zuletzt von Andy (13. Oktober 2013 um 11:17)

  • Wenn, dann nimm AssembleIt2, das hat einige Features die das "inlinen" schon sehr nahe bringen, bspw. fällt das gesamte FASM-UDF-Gedöns mit Funktionen usw. weg, aus

    [autoit]

    _FASMADD("move eax,ebx")
    ;wird
    mov eax, ebx

    [/autoit]

    Bei Syntaxfehlern im asm-code wird in Scite direkt in die entsprechende Zeile gesprungen, AutoItvariablen können einfach in den Asm-code geschrieben werden uswusf...spiel mal bissl rum^^

    Du kannst auch am Anfang des Scriptes zum Testen per AssembleIt2() den Binär-Code erstellen lassen und später nur mit dllcalladdress() arbeiten, dann musst du, um AssembleIt() wegfallen zu lassen nur noch eine Zeile ändern, s. Beispiel.
    autoit.de/wcf/attachment/23626/

  • Hi,

    Zitat

    auch wenn es eigentlich dazu dienen sollte inline C++ einzuführen.

    den Versuch gab es ja schon einmal hier

    Im Prinzip müsste man nur den C-Compiler includen statt des Assemblers und AssembleIt2() würde zu CIt() :rolleyes:

  • Ich habe den VC++ Compiler benutzt, aber ich habe nicht geschafft die .obj Datei richtig zu parsen (für jeden Fall, einzelne Fälle mit denen ich gearbeitet hab, hat es funktioniert). Im Endeffekt hatte ich keine Lust mehr, weil der Code eh nur reines C++ ohne includes und ohne selbst alloziierte Daten ging.