Bitmap invertieren

  • Hey Autoitte,
    ich komme mal direkt auf den Punkt:
    Weiß einer von euch wie man eine HBITMAP variable, in der ich vorher mit _GDIPlus_ImageLoadFromFile(),
    die Farben in dieser Bitmap umkehren kann?

    Meine Werke


    EasyScreen (teilweise Crocoframe): <EasyScreen>
    Spotify2Title: <Spotify2Title> (zur Info, dies ist KEIN Spotify-Downloader! Wer das dachte... :rofl: sowas gibts hier nicht :thumbdown: )

  • Hier mal ein beispiel mit einer FarbMatrix ;).

    Spoiler anzeigen
    [autoit]

    #include <GUIConstants.au3>
    #include <GDIP.au3>

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

    $hWnd = GUICreate("Inverted Bitmap by name22 (autoit.de)", 600, 400)
    GUISetState()

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

    _GDIPlus_Startup()

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

    $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd)
    $hInvIA = _GDIPlus_ImageAttributesCreate()

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

    $hImage = _GDIPlus_ImageLoadFromFile(FileOpenDialog("Bild auswählen", @ScriptDir, "All Images (*.*)"))
    $aImageDim = _GDIPlus_ImageGetDimension($hImage)

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

    $tColorMatrix = _GDIPlus_ColorMatrixCreateNegative()
    $pColorMatrix = DllStructGetPtr($tColorMatrix)

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

    _GDIPlus_ImageAttributesSetColorMatrix($hInvIA, 0, True, $pColorMatrix)

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

    _GDIPlus_GraphicsDrawImageRectRectIA($hGraphic, $hImage, 0, 0, $aImageDim[0], $aImageDim[1], 0, 0, 600, 400, $hInvIA)

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

    While GUIGetMsg() <> $GUI_EVENT_CLOSE
    WEnd

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

    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_ImageAttributesDispose($hInvIA)
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()

    [/autoit]


    Das ist wesentlich schneller als die Pixel mit BitmapLockBits o.Ä. zu bearbeiten. :P
    Allerdings wird dazu noch die GDIP.au3 benötigt die hier irgendwo im Forum herumschwirrt :S .

  • eine Steilvorlage für Assemblerprogrammierer (oder die, die es werden wollen^^)
    Invertieren heisst auf Bitmapbearbeiterisch nämlich XOR 0xFFFFFF
    Das geht natürlich auch Pixel für Pixel in AutoIt, oder aber in Assembler 8o
    per XOR kann man nämlich direkt Speicherzellen bearbeiten (in diesem Fall invertieren)
    XOR [Speicher],0xFFFFFF
    invertiert somit das Pixel. Mit das einfachste, was man in Assembler machen kann

    Spoiler anzeigen
    [autoit]

    ;#include <AssembleIt.au3>
    #include <GDIPlus.au3>

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

    _GDIPlus_Startup()
    $file = FileOpenDialog("Grafikdatei öffnen", @ScriptDir, "Bilder (*.jpg;*.bmp;*.png)")
    If @error Then Exit
    $hBitmap = _GDIPlus_BitmapCreateFromFile($file)
    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeight = _GDIPlus_ImageGetHeight($hBitmap)

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

    $hBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    $Scan = DllStructGetData($hBitmapData, "Scan0") ;Pointer auf bitmapdaten(Pixel)

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

    ;$ret=_AssembleIt("ptr","_invertieren","ptr", $scan, "int", $iWidth * $iHeight) ;ptr als Rückgabe, um die hexzahlen schön zu sehen
    ;oder
    Global $tCodeBuffer = DllStructCreate("byte[23]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, "0x8B7424048B4C24088136FFFFFF0083C60483E90177F2C3") ;write opcodes into memory
    $ret = DllCall("user32.dll", "ptr", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), "ptr", $Scan, "int", $iWidth * $iHeight, "int", 0, "int", 0)

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $hBitmapData)
    _GDIPlus_ImageSaveToFile($hBitmap, "invert.jpg")

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

    ShellExecute("invert.jpg")

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

    ;~ Func _invertieren()
    ;~ _("use32") ;sollte immer eingesetzt werden!

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

    ;~ _("mov esi,dword[esp+4]") ;Startadresse Bitmapdaten (Pixel)
    ;~ _("mov ecx,dword[esp+8]") ;anzahl Pixel

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

    ;~ _("_schleife:") ;so lange, bis ecx=0
    ;~ _("xor dword[esi],0xFFFFFF") ;Pixel invertieren
    ;~ _("add esi,4") ;adresse nächstes Pixel
    ;~ _("sub ecx,1") ;schleifendurchgang
    ;~ _("ja _schleife") ;so lange, bis ecx=0
    ;~ _("ret ")
    ;~ EndFunc ;==>_invertieren

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

    Geht auch bissl kürzer, aber so ist es verständlicher

    /EDIT/ Funktioniert das so eigentlich auch in 64Bit BS?

  • Danke Oscar, liegt wohl daran, dass es bei 64Bit zwei der "user.dll" gibt....und ich die "falsche" benutze

  • Ich seh schon, wird Zeit, dass ich mir mal ein 64Bit BS besorge....thx fürs Testen jedenfalls!

  • Sieht alles super aus :thumbup: :thumbup: :thumbup: ,
    aber...
    jetzt kommt die schwierigkeit:
    ich möchte ein Bild auf mein Desktop malen, welches nur schwarz enthält (.png)
    Diese ist ein Fadenkreuz und im Anhang ;)
    Es sollte dann immer sichtbar sein ^^

    //Edit
    Das Bild sollte nicht Flackern, also sollte es in einem Gui sein. Eingebunden hab ich es bisher immer mit dieser Funktion:

    Spoiler anzeigen
    [autoit]

    Func SetBitmap($hGUI, $hImage, $iOpacity) ; lade hintergrundbild und mache den hintergrund transparent Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend
    $hScrDC = _WinAPI_GetDC(0) $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC) $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage) $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap) $tSize = DllStructCreate($tagSIZE) $pSize = DllStructGetPtr($tSize) DllStructSetData($tSize, "X", _GDIPlus_ImageGetWidth($hImage)) DllStructSetData($tSize, "Y", _GDIPlus_ImageGetHeight($hImage)) $tSource = DllStructCreate($tagPOINT) $pSource = DllStructGetPtr($tSource) $tBlend = DllStructCreate($tagBLENDFUNCTION) $pBlend = DllStructGetPtr($tBlend) DllStructSetData($tBlend, "Alpha", $iOpacity) DllStructSetData($tBlend, "Format", $AC_SRC_ALPHA) _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA) _WinAPI_ReleaseDC(0, $hScrDC) _WinAPI_SelectObject($hMemDC, $hOld) _WinAPI_DeleteObject($hBitmap) _WinAPI_DeleteDC($hMemDC)EndFunc

    [/autoit]
  • Zitat

    Da bin ich bei meinem ASM-Inverter nicht drauf gekommen

    Tröste dich, ich hab auch erst von jedem Byte 255 abgezogen :D

  • Als reiner AutoItcode, bissl langsamer

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>

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

    _GDIPlus_Startup()
    $file = FileOpenDialog("Grafikdatei öffnen", @ScriptDir, "Bilder (*.jpg;*.bmp;*.png)")
    If @error Then Exit
    $hBitmap = _GDIPlus_BitmapCreateFromFile($file)
    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeight = _GDIPlus_ImageGetHeight($hBitmap)

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

    $hBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32RGB)
    $Scan = DllStructGetData($hBitmapData, "Scan0") ;Pointer auf bitmapdaten(Pixel)
    $struct = DllStructCreate("dword [" & $iWidth * $iHeight & "]", $Scan) ;struct erstellen, um mit AutoIt die Daten zu bekommen

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

    For $i = 1 To $iWidth * ($iHeight - 1) ;jedes pixel
    $pixel = BitXOR(DllStructGetData($struct, 1, $i), 0xFFFFFF) ;lesen und XORen
    DllStructSetData($struct, 1, $pixel, $i) ;Pixel schreiben
    Next

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

    _GDIPlus_BitmapUnlockBits($hBitmap, $hBitmapData)
    _GDIPlus_ImageSaveToFile($hBitmap, "invert.jpg")

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

    ShellExecute("invert.jpg")

    [/autoit]
  • Eigentlich wollte ich eine Lösung mit _WinAPI_BitBlt posten, aber ich häng da schon seit gestern fest.
    Mittlerweile hab ich so viel ein und ausgebaut, dass ich garnicht mehr weiß, wie es ursprünglich aussehen sollte.

    Mag mir vielleicht jemand erklären, was ich falsch gemacht habe?

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>

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

    _GDIPlus_Startup()

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

    $sFile = FileOpenDialog("", "", "Alle (*.*)")
    If @error Then Exit

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

    $hBild = _GDIPlus_BitmapCreateFromFile($sFile)
    $hBild_Inverted = BitmapInvert($hBild)

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

    _GDIPlus_ImageSaveToFile($hBild_Inverted, StringTrimRight($sFile, 4) & "_inverted.bmp")

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

    Func BitmapInvert($hBitmap)
    Local $iWidth, $iHeigth, $hBitmap_New, $hBitmap_Old
    Local $hDC_Source, $hDC_Dest, $vReturn

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

    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeigth = _GDIPlus_ImageGetHeight($hBitmap)
    ;~ $hBitmap_New = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iWidth, $iHeigth)

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

    $hDC_Source = _WinAPI_CreateCompatibleDC(0)
    $hDC_Dest = _WinAPI_CreateCompatibleDC(0)

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

    $hBitmap_New = _WinAPI_CreateCompatibleBitmap($hDC_Dest,$iWidth,$iHeigth)
    $hBitmap_Old = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)

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

    $vReturn = _WinAPI_SelectObject($hDC_Dest, $hBitmap_New) ; nur für's debug in Var schreiben
    $vReturn = _WinAPI_SelectObject($hDC_Source, $hBitmap_Old) ; nur für's debug in Var schreiben
    $vReturn = _WinAPI_BitBlt($hDC_Dest, 0, 0, $iWidth, $iHeigth, $hDC_Source, 0, 0, $SRCINVERT)

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

    ConsoleWrite(_WinAPI_GetLastError() & @CRLF)

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

    $vReturn = _WinAPI_DeleteDC($hDC_Source)
    $vReturn = _WinAPI_DeleteDC($hDC_Dest)

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

    Return _GDIPlus_BitmapCreateFromHBITMAP($hBitmap_New)
    EndFunc ;==>BitmapInvert

    [/autoit]
  • Hi Seubo, Deine Idee war klasse!
    Allerdings hattest du per XOR deine dest_bitmap verknüpfen wollen. In dieser ist aber, da neu angelegt, jedes Pixel 0x00000000. Um zu invertieren, müsstest du erst das dest_Bitmap mit 0xFFFFFFFF füllen!
    Oder du machst es wie der faule Andy, der benutzt beim Blitten den Parameter

    Zitat von Hilfe zu Bitblt

    $DSTINVERT - Invertiert die Farben des Ziel-Rechtecks (umkehren)


    und spart sich so die 2. Bitmap.

    Spoiler anzeigen
    [autoit]

    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>

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

    _GDIPlus_Startup()

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

    $sFile = FileOpenDialog("", "", "Alle (*.*)")
    If @error Then Exit

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

    $hBild = _GDIPlus_BitmapCreateFromFile($sFile)
    $hBild_Inverted = BitmapInvert($hBild)

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

    FileDelete(StringTrimRight($sFile, 4) & "_inverted.bmp")
    $s = _GDIPlus_ImageSaveToFile($hBild_Inverted, StringTrimRight($sFile, 4) & "_inverted.bmp")
    ShellExecute(StringTrimRight($sFile, 4) & "_inverted.bmp")

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

    Func BitmapInvert($hBitmap)
    Local $iWidth, $iHeigth, $hBitmap_New, $hBitmap_Old
    Local $hDC_Source, $hDC_Dest, $vReturn

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

    $iWidth = _GDIPlus_ImageGetWidth($hBitmap)
    $iHeigth = _GDIPlus_ImageGetHeight($hBitmap)

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

    $hDC_Source = _WinAPI_CreateCompatibleDC(0)
    $hBitmap_Old = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap)

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

    $vReturn = _WinAPI_SelectObject($hDC_Source, $hBitmap_Old) ; nur für's debug in Var schreiben
    $vReturn = _WinAPI_BitBlt($hDC_Source, 0, 0, $iWidth, $iHeigth, $hDC_Source, 0, 0, $dstinvert)
    $vReturn = _WinAPI_DeleteDC($hDC_Source)
    Return _GDIPlus_BitmapCreateFromHBITMAP($hBitmap_Old)
    EndFunc ;==>BitmapInvert

    [/autoit] [autoit][/autoit] [autoit][/autoit]
  • Einfach, schnell und effizient! :thumbup:

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • ich möchte ein Bild auf mein Desktop malen, welches nur schwarz enthält (.png)
    Diese ist ein Fadenkreuz und im Anhang ;)
    Es sollte dann immer sichtbar sein

    Hier, das hab ich mal auf die Schnelle zusammengeschustert.
    Es geht mit Sicherheit auch anders (evtl. auch einfacher), aber ich hatte so ein ähnliches Script schon hier
    rumliegen - da ging es darum, wie mit einem Rotstift auf den Bildschirm zu malen.

    Spoiler anzeigen
    [autoit]


    #include <GDIPlus.au3>
    #include <WindowsConstants.au3>

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

    ;Variablen deklarieren
    Local $hGUI, $hGraphics
    Local $aMPos, $aMPos_Old[4]
    Local $iCH_Width, $iCH_Height

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

    ;GDI+ initialisieren öffnen und ESC als HotKey festlegen
    _GDIPlus_Startup()
    HotKeySet("{ESC}", "_Exit")

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

    ;Bild (fadenkreuz) laden
    $hCrossHair = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\crosshair.png")
    If Not $hCrossHair Then Exit MsgBox(0,"",StringFormat("'%s' konnte nicht geunden werden.",@ScriptDir & "\crosshair.png"))

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

    $iCH_Width = _GDIPlus_ImageGetWidth($hCrossHair)
    $iCH_Height = _GDIPlus_ImageGetHeight($hCrossHair)

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

    ;Transparente GUI erstellen
    $hDummy = GUICreate("") ; Dummy GUI, damit richtiges nicht in Taskleiste auftaucht.
    $hGUI = GUICreate("", $iCH_Width, $iCH_Height, 0, 0, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST, $WS_EX_TRANSPARENT), $hDummy)
    GUISetBkColor(0xABCDEF, $hGUI)
    _WinAPI_SetLayeredWindowAttributes($hGUI, 0xABCDEF, 255)

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

    ;Graphic-Objekt erstellen und GUI anzeigen
    $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    GUISetState()

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

    _GDIPlus_GraphicsDrawImage($hGraphics,$hCrossHair,0,0)

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

    While Sleep(10)
    $aMPos = MouseGetPos()
    If $aMPos[0] <> $aMPos_Old[0] Or $aMPos[1] <> $aMPos_Old[1] Then
    WinMove($hGUI, "", $aMPos[0] - $iCH_Width / 2, $aMPos[1] - $iCH_Height / 2)
    If ($aMPos[0] + $iCH_Width > @DesktopWidth) Or ($aMPos[1] + $iCH_Height > @DesktopHeight) Or ($aMPos[0] - $iCH_Width < 0) Or ($aMPos[1] - $iCH_Height < 0) Then _GDIPlus_GraphicsDrawImage($hGraphics,$hCrossHair,0,0)
    $aMPos_Old = $aMPos
    EndIf

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

    WEnd

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

    Func _Exit()
    _GDIPlus_GraphicsDispose($hGraphics)
    GUIDelete($hGUI)
    Exit
    EndFunc ;==>_Exit

    [/autoit]