Hallo zusammen!
Das Skript ist nur ein kleiner Auszug und ist entsprechend angepasst worden, damit es lauffähig ist.
Folgendes Problem tritt bei mir auf:
Sobald ich mit dem Mausrad durch ein zuvor gewähltes Bilderverzeichnis browse und ein angezeigtes Bild eine andere Auflösung hat als sein Vorgänger, wird es nur kurz dargestellt und verschwindet dann. Was bleibt, ist eine graue Fläche in der Größe des neuen Bildes.
Die folgenden Bilder werden wieder normal angezeigt, bis die Bildauflösung wechselt., dann tritt wieder der selbe Fehler auf.
Das geschieht zwar auch, wenn das Mausrad durch die Curser-Tasten "emuliert" wird (Funktionen: _Up und _Down), aber das Bild wird letztendlich doch angezeigt.
-> bei Tastendruck wird die ?selbe? Nachricht an die Funktion _WM_MOUSEWHEEL gesendet wie bei Benutzung des Mausrades.
Ich bin mir nicht sicher, ob es sich wirklich um ein GDI+ Problem handelt, oder ob ich einfach irgendwo 'nen Denkfehler eingebaut habe.
Also habe ich folgende Fragen:
1. Wieso werden Bilder bei Benutzung des Mausrades nicht angezeigt, wenn die Auflösung wechselt? (Fenster wird mit WinMove angepasst)
1.1. Nachtrag: Jetzt mußte ich feststellen, dass der Fehler auch beim Minimieren und Wiederherstellen des Fensters auftritt (Maus-Klick aufs Taskbar-Icon). Die WM_PAINT Message wird aber laut Konsole verarbeitet und funktioniert auch noch, wenn man das Bild anschliessend bewegt.
2. Wie kann ich erreichen, dass das vorige Bild erst gelöscht wird, wenn das neue Bild im Buffer zur Verfügung steht und geblittet werden kann, so dass bei großen Bildern nicht zuerst diese graue Fläche zu sehen ist?
Weitere Fragen:
3. Wie kann ich ein Kontext Menü (Zeilen 84-87) auf das Fenster (Bild) legen? Die Möglichkeit, das Fenster mit der Maus zu bewegen darf nicht wegfallen.
4. Gibt es eine Methode, das Flackern (Tearing) beim Bildwechsel zu beseitigen? Eigendlich dachte ich, dass das durch die Benutzung des Buffers geschieht, was aber nicht der Fall ist. Stickwort: vSync?
5. Durch die Benutzung des Buffers und WM_PAINT wird das Bild schon relativ schnell dargestellt, wenn sich Teile ausserhalb des Bildschirms befinden (deutlich schneller, als ohne Buffer). Geht das noch schneller, da es immer noch hakt?
Da die beschriebenen "Effekte" besonders deutlich bei sehr großen Bildern zu sehen sind habe ich eine Funktion eingebaut, einige aus dem Internet zu laden, falls lokal keine vorhanden sind (Abbruch klicken bei Verzeichnisauswahl).
Und noch eine allgemeine Frage:
6. Hat noch jemand Tips, die Bilddarstellung zu optimieren, um noch mehr Geschwindigkeit rauszuholen?
Und nach soviel Text, hier noch das Skript:
Spoiler anzeigen
#include <GDIPlus.au3>
#include <GuiConstantsEx.au3>
#include <Array.au3>
#include <File.au3>
#include <WindowsConstants.au3>
Opt("GUIOnEventMode", 1)
Opt("MustDeclareVars", 1)
OnAutoItExitRegister("_Exit")
[/autoit] [autoit][/autoit] [autoit]Global $hBitmap, $hBuffer, $path, $aFileList, $hGUI, $hImage, $iCounter, $hGraphic, $iScreenHeight, $idMsg, $iScreenWidth, $aPos[2] = [0, 0]
[/autoit] [autoit][/autoit] [autoit]Do
$path = FileSelectFolder("Bilder-Verzeichnis wählen", "")
If @error Then ;==> Bilderdownload anbieten, wenn kein Verzeichnis gewählt wird
$idMsg = MsgBox(36, "Bilder laden...?", "Kein Verzeichnis gewählt. " & _
"Sollen einige (große) Bilder von der Seite interfacelift.com geladen werden?" & @CRLF & _
"Die 5 höchstbewerteten Bilder mit ~3840 Pixeln Breite (ca. 21 MB)")
If $idMsg = 6 Then
$path = @TempDir & "\_temp"
DirCreate($path)
InetGet("http://interfacelift.com/wallpaper/7yz4ma1/02348_caltonhilledinburgh_3840x1024.jpg", $path & "\Bild1.jpg")
InetGet("http://interfacelift.com/wallpaper/7yz4ma1/03309_damnationcreektrail_3840x2400.jpg", $path & "\Bild2.jpg")
InetGet("http://interfacelift.com/wallpaper/7yz4ma1/02544_aniconfromanewangle_3840x1024.jpg", $path & "\Bild3.jpg")
InetGet("http://interfacelift.com/wallpaper/7yz4ma1/02554_shaftwoods_3840x1024.jpg", $path & "\Bild4.jpg")
InetGet("http://interfacelift.com/wallpaper/7yz4ma1/02367_easternshoresunrise_3840x1024.jpg", $path & "\Bild5.jpg")
Else
Exit
EndIf
EndIf
;$aFileList = _FileListToArray($path, "*.jpg")
$aFileList = _FileListToArrayMultiSelect($path, "*.gif,*.ico,*.jpg,*.jpeg,*.png,*.bmp", ",", 1)
If $aFileList[0] = 0 Then MsgBox(16, "Keine Bilder", "Das Verzeichnis enthält keine Bilder." & @CRLF & "Bitte in folgendem Fenster ein korrektes Verzeichnis auswählen oder Abbrechen wählen...", 5)
Until $aFileList[0] <> 0
$hGUI = GUICreate("Show PIC", Default, Default, Default, Default, $WS_POPUP + $WS_MINIMIZEBOX)
[/autoit] [autoit][/autoit] [autoit]GUISetState()
[/autoit] [autoit][/autoit] [autoit]_GDIPlus_Startup()
[/autoit] [autoit][/autoit] [autoit]_Benchmark() ;==> einmal alle Bilder eines Verzeichnisses anzeigen und benötigte Zeit in der Konsole ausgeben
[/autoit] [autoit][/autoit] [autoit]GUIRegisterMsg($WM_LBUTTONDOWN, "_WM_LBUTTONDOWN") ;==> Fenster mit Maus bewegen
GUIRegisterMsg($WM_PAINT, "_WM_PAINT") ;==> Bild neu zeichnen, wenn es (teilweise) ausserhalb des Bildschirms war
GUIRegisterMsg($WM_MOUSEWHEEL, "_WM_MOUSEWHEEL") ;==> nächstes/vorheriges Bild
GUIRegisterMsg($WM_ACTIVATE, "_WM_ACTIVATE") ;==> Curser Tasten (Up/Down) nur abfragen, wenn Fenster aktiv
GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")
[/autoit] [autoit][/autoit] [autoit]HotKeySet("{UP}", "_Up")
HotKeySet("{DOWN}", "_Down")
While Sleep(1000)
WEnd
Func _Show()
HotKeySet("{UP}") ;==> keine Tasten abfangen während das Bild angezeigt wird, dadurch werden Bilder nur fortlaufend angezeigt, solange die Taste gedrückt ist
HotKeySet("{DOWN}") ;==> wichtig zB bei sehr großen Bildern
Local $iWidth, $iHeight, $paint, $idExit, $idContext, $idDummy, $hRgn
Local Static $bDurchlauf = False
Local Static $iScreenWidthOld, $iScreenHeightOld
$hImage = _GDIPlus_ImageLoadFromFile($path & "\" & $aFileList[$iCounter])
[/autoit] [autoit][/autoit] [autoit]$iHeight = _GDIPlus_ImageGetHeight($hImage)
$iWidth = _GDIPlus_ImageGetWidth($hImage)
$iScreenHeight = Int(($iHeight * (@DesktopHeight / $iHeight)) - 40) ; Bild immer an Bildschirmhöhe anpassen minus Taskbar (Win 7, Taskbar unten)
$iScreenWidth = Int($iWidth * ($iScreenHeight / $iHeight)) ; Anpassung an individuelle Konfiguration folgt
;ConsoleWrite($iScreenWidth & " X " & $iScreenHeight & @CRLF)
;ConsoleWrite($aFileList[$iCounter] & @CRLF)
Switch $bDurchlauf ;==> werden die Grafiken direkt nach Anzeige gelöscht, hat WM_PAINT nichts zum anzeigen, daher in dieser Reihenfolge
Case False ;==> nur beim ersten Durchlauf ausführen
WinMove("Show PIC", "", $aPos[0], $aPos[1], $iScreenWidth, $iScreenHeight)
#cs ; Wenn das Menü aktiviert ist, funktioniert das Bewegen des Fensters mit der Maus nicht mehr. Wie gehts richtig???
$idDummy = GUICtrlCreateLabel("", 0, 0, $iScreenWidth, $iScreenHeight)
$idContext = GUICtrlCreateContextMenu($idDummy)
$idExit = GUICtrlCreateMenuItem("Beenden", $idContext)
GUICtrlSetState($idExit, "_Exit")
#ce
$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
$hBitmap = _GDIPlus_BitmapCreateFromGraphics($iScreenWidth, $iScreenHeight, $hGraphic)
$hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
$bDurchlauf = True
Case Else ;==> darf beim ersten Durchlauf nicht ausgeführt werden
Select
Case $iScreenWidthOld <> $iScreenWidth Or $iScreenHeightOld <> $iScreenHeight ;==> nur ausführen, wenn sich die Bildgröße ändert
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_GraphicsDispose($hBuffer)
_GDIPlus_BitmapDispose($hBitmap)
WinMove("Show PIC", "", $aPos[0], $aPos[1], $iScreenWidth, $iScreenHeight)
$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
$hBitmap = _GDIPlus_BitmapCreateFromGraphics($iScreenWidth, $iScreenHeight, $hGraphic)
$hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
EndSelect
EndSwitch
;WinMove("Show PIC", "", $aPos[0], $aPos[1], $iScreenWidth, $iScreenHeight)
;$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
;$hBitmap = _GDIPlus_BitmapCreateFromGraphics($iScreenWidth, $iScreenHeight, $hGraphic)
;$hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap)
_GDIPlus_GraphicsDrawImageRect($hBuffer, $hImage, 0, 0, $iScreenWidth, $iScreenHeight)
$paint = _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $iScreenWidth, $iScreenHeight)
If $paint = False Then Beep(1500, 50) ;nur zum debuggen
_GDIPlus_ImageDispose($hImage)
[/autoit] [autoit][/autoit] [autoit]$iScreenWidthOld = $iScreenWidth
$iScreenHeightOld = $iScreenHeight
HotKeySet("{UP}", "_Up")
HotKeySet("{DOWN}", "_Down")
EndFunc ;==>_Show
Func _WM_PAINT() ;==> ist das die beste/schnellste Lösung???
_GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $iScreenWidth, $iScreenHeight)
ConsoleWrite("WM_PAINT: " & $aFileList[$iCounter] & " -> " & $aPos[0] & "/" & $aPos[1] & " -> " & $iScreenHeight & " X " & $iScreenWidth & @CRLF)
EndFunc ;==>_WM_PAINT
Func _Benchmark()
Local $begin
$begin = TimerInit()
For $i = 1 To $aFileList[0]
;0x00780000, 0xFF880000
_WM_MOUSEWHEEL(Default, Default, 0xFF880000, Default)
Next
ConsoleWrite(TimerDiff($begin) / 1000 & @CRLF)
;MsgBox(0, "", TimerDiff($begin) / 1000)
EndFunc ;==>_Benchmark
Func _Down()
_WM_MOUSEWHEEL(Default, Default, 0xFF880000, Default)
EndFunc ;==>_Down
Func _Up()
_WM_MOUSEWHEEL(Default, Default, 0x00780000, Default)
EndFunc ;==>_Up
Func _WM_MOUSEWHEEL($hWnd, $iMsg, $iWParam, $iLParam)
;#forceref $hWnd, $iMsg, $iWParam, $iLParam
Local $_iWParam
ConsoleWrite($hWnd & "->" & $iMsg & "->" & $iWParam & "->" & $iLParam & "->>" & BitShift($iWParam, 16) & "->" & $iCounter & @CRLF)
$_iWParam = BitShift($iWParam, 16) ;mögliche Werte für $iWParam 0x00780000, 0xFF880000
If $_iWParam < 0 Then ; If $iWParam = 0xFF880000 Then
$iCounter += 1
If $iCounter > $aFileList[0] Then $iCounter = 1
Else
$iCounter -= 1
If $iCounter < 1 Then $iCounter = $aFileList[0]
EndIf
_Show()
EndFunc ;==>_WM_MOUSEWHEEL
Func _FileListToArrayMultiSelect($dir, $searchlist, $Separator, $iFlag = 0)
Local $aFileList[1] = [0], $aFileList1, $iN, $Num, $search
$search = StringSplit($searchlist, $Separator)
If $search[0] > 0 Then
For $iN = 1 To $search[0]
$aFileList1 = _FileListToArray($dir, $search[$iN], $iFlag)
If Not @error Then
$Num = UBound($aFileList)
_ArrayConcatenate($aFileList, $aFileList1)
$aFileList[0] = $aFileList[0] + $aFileList[$Num]
_ArrayDelete($aFileList, $Num)
EndIf
Next
EndIf
Return $aFileList
EndFunc ;==>_FileListToArrayMultiSelect
Func _WM_ACTIVATE()
Switch WinActive("Show PIC")
Case 0
HotKeySet("{UP}")
HotKeySet("{DOWN}")
Case Else
HotKeySet("{UP}", "_Up")
HotKeySet("{DOWN}", "_Down")
EndSwitch
EndFunc ;==>_WM_ACTIVATE
Func _WM_LBUTTONDOWN($hWnd, $iMsg, $wParam, $lParam) ;==> Fenster mit Maus bewegen
If BitAND(WinGetState($hWnd), 32) Then Return $GUI_RUNDEFMSG
DllCall("user32.dll", "long", "SendMessage", "hwnd", $hWnd, "int", $WM_SYSCOMMAND, "int", 0xF009, "int", 0)
$aPos = WinGetPos("Show PIC")
EndFunc ;==>_WM_LBUTTONDOWN
Func _Exit()
Local $delete
_GDIPlus_GraphicsDispose($hGraphic)
_GDIPlus_GraphicsDispose($hBuffer)
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
If FileExists(@TempDir & "\_temp\") Then
$delete = DirRemove(@TempDir & "\_temp\", 1)
If $delete = 0 Then MsgBox(16, "Fehler", "Das temporäre Verzeichnis '" & @TempDir & "\_temp\" & "konnte nicht gelöscht werden.")
EndIf
Exit
EndFunc ;==>_Exit
Vielen Dank an alle, die bereit sind, sich freiwillig durch den Text und das Skript zu quälen...
Sanfte Grüße