_WinAPI_BitBlt und Ausgabe in Bild-Datei

  • Ich hab ein kleines Script welches mir .csv-Dateien grafisch darstellt. Den erzeugten Graph kann ich über GDI+ mir in eine Bild-Datei ausgeben lassen, doch leider fehlen mir da dann die Achsbeschriftungen, da diese aus Text/Label bestehen. Wenig sinnvoll :( Screencapture fällt leider aus, da die Diagramme mit 5760x600 Pixeln wesentlich breiter wie ein Monitor sind.

    Auf Tip und Hinweis von name22 hab ich dann mal mit _WinAPI_BitBlt herum probiert, doch irgendwie krieg ich es nicht gebacken es in eine Bild-Datei auszugeben, geschweige denn das überhaupt eine Datei erzeugt wird :pinch:

    [autoit]


    $hDC_GUI = _WinAPI_GetDC($GUI_Graph)
    $hGraphics = _WinAPI_BitBlt($hDC_GUI, 0, 0, $GraphSizeX, $GraphSizeY, $hDC_GUI, 0, 0, $SRCCOPY)
    $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hGraphics)
    _GDIPlus_ImageSaveToFile($hImage, @ScriptDir &"\test.jpg")

    [/autoit]

    Wo liegt hier der Hund begraben? Ich find es einfach nicht..

    Einmal editiert, zuletzt von Flitzpiepe84 (20. März 2014 um 16:52)

  • Schau dir mal den Rueckgabewert von _WinAPI_BitBlt an ;)
    Ausserdem kopierst du den Auschnitt vom GUI-DC zum GUI-DC. Schau dir die Funktion einfach nochmal in der Hilfe genau an

    There's a joke that C has the speed and efficieny of assembly language combined with readability of....assembly language. In other words, it's just a glorified assembly language. - Teh Interwebz

    C makes it easy to shoot yourself in the foot; C++ makes it harder, but when you do, you blow off your whole leg. - Bjarne Stroustrup
    Genie zu sein, bedeutet für mich, alles zu tun, was ich will. - Klaus Kinski

  • okay, ich seh es grade.
    _WinAPI_BitBlt gibt nur True oder False zurück. Und der erste Paramter ist mein Ziel-Handle. Das Ziel-Handle müsste wiederum daraus bestehen das es aus dem Inhalt eine Bild-Datei erzeugt. <-- So oder ähnlich sollte es doch funktionieren?
    Aber ich raff momentan einfach nicht wie dieses Ziel-Handle aussehen soll ?(

  • Das erste Beispiel (aus der Onlinehilfe) sollte allgemein verständlich sein. Wie PainTrain schon gesagt hat schau dir die Hilfe an, oder andere scripte welche die funktion enthalten es wird nicht immer jemand vorhanden sein der einem bei seinen problemen hilft. Hilfe zur selbsthilfe ist die beste Hilfe :thumbup:
    Online Hilfe:

    Spoiler anzeigen
    [autoit]


    #include <WinAPI.au3>
    #include <WindowsConstants.au3>

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

    $hGui = GUICreate("_WinAPI_BitBlt", 640, 480)
    GUISetState()
    $hWnd = _WinAPI_GetDesktopWindow()

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

    $hDC_Gui = _WinAPI_GetDC($hGui)
    $hDC_Desk = _WinAPI_GetDC($hWnd)

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

    _WinAPI_BitBlt($hDC_Gui, 0, 0, 640, 480, $hDC_Desk, 0, 0, $SRCINVERT)
    _WinAPI_BitBlt($hDC_Gui, 100, 100, 320, 240, $hDC_Desk, 0, 0, $SRCCOPY)

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

    While GUIGetMsg() <> -3
    Sleep(10)
    WEnd

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

    _WinAPI_ReleaseDC($hGui, $hDC_Gui)
    _WinAPI_ReleaseDC($hWnd, $hDC_Desk)

    [/autoit]


    2. Beispiel Englische Hilfe

    Spoiler anzeigen
    [autoit]

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

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

    Example()

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

    Func Example()
    AutoItSetOption("GUIOnEventMode", 1)

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

    _GDIPlus_Startup() ;initialize GDI+
    Local Const $iWidth = 600, $iHeight = 600, $iBgColor = 0x202020 ;$iBGColor format RRGGBB

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

    Global $hGUI = GUICreate("GDI+ example", $iWidth, $iHeight) ;create a test GUI
    GUISetBkColor($iBgColor, $hGUI) ;set GUI background color
    GUISetState(@SW_SHOW)

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

    ;create a faster buffered graphics frame set for smoother gfx object movements
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight) ;create an empty bitmap
    Global $hHBITMAP = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) ;convert GDI+ bitmap to GDI bitmap
    _GDIPlus_BitmapDispose($hBitmap) ;delete GDI+ bitmap because not needed anymore

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

    Global $hDC = _WinAPI_GetDC($hGUI) ;get device context from GUI
    Global $hDC_Backbuffer = _WinAPI_CreateCompatibleDC($hDC) ;creates a memory device context compatible with the specified device
    Global $DC_Obj = _WinAPI_SelectObject($hDC_Backbuffer, $hHBITMAP) ;selects an object into the specified device context
    Global $hGfxCtxt = _GDIPlus_GraphicsCreateFromHDC($hDC_Backbuffer) ;create a graphics object from a device context (DC)
    _GDIPlus_GraphicsSetSmoothingMode($hGfxCtxt, $GDIP_SMOOTHINGMODE_HIGHQUALITY) ;set smoothing mode (8 X 4 box filter)
    _GDIPlus_GraphicsSetPixelOffsetMode($hGfxCtxt, $GDIP_PIXELOFFSETMODE_HIGHQUALITY)

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

    Global $hPen = _GDIPlus_PenCreate() ;create a pen object

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

    GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")

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

    Local Const $iDeg = ACos(-1) / 180 ;ACos(-1) is nearly pi
    Local $iX_Center = $iWidth / 2, $iY_Center = $iHeight / 2, $iXPos, $iYPos, $iAngle = 0, $iRound = 0
    Local Const $iDots = 7, $iAngelDist = 360 / $iDots, $iRadius = 250
    Local $aCoordinates[$iDots][2] ;create an array to save coordinates of the x/y coordinates
    Do
    _GDIPlus_GraphicsClear($hGfxCtxt, 0xFF000000 + $iBgColor) ;clear bitmap with given color (AARRGGBB format)
    For $i = 0 To $iDots - 1
    $iXPos = $iX_Center + Cos($iAngle * $iDeg) * $iRadius ;calculate x position
    $iYPos = $iY_Center + Sin($iAngle * $iDeg) * $iRadius ;calculate y position
    $aCoordinates[$i][0] = $iXPos
    $aCoordinates[$i][1] = $iYPos
    _GDIPlus_PenSetColor($hPen, 0xFFFFFF00) ;set pen color for inner lines
    _GDIPlus_PenSetWidth($hPen, 2) ;set pen size for outer lines
    _GDIPlus_GraphicsDrawLine($hGfxCtxt, $aCoordinates[$i][0], $aCoordinates[$i][1], _ ;draw inner lines
    $aCoordinates[Mod(($i + $iDots / 2), $iDots)][0], $aCoordinates[Mod(($i + $iDots / 2), $iDots)][1], $hPen) ;draw to opposite side
    _GDIPlus_PenSetColor($hPen, 0xFFFF8000) ;set pen color
    _GDIPlus_PenSetWidth($hPen, 3) ;set pen size
    ;array of coordinates should be filled before first draw to screen
    If $i < $iDots - 1 Then _GDIPlus_GraphicsDrawLine($hGfxCtxt, $aCoordinates[$i][0], $aCoordinates[$i][1], $aCoordinates[$i + 1][0], $aCoordinates[$i + 1][1], $hPen) ;;draw outer lines
    $iAngle += $iAngelDist ;increase angle to next dot
    Next
    ;draw last line to 1st line
    _GDIPlus_GraphicsDrawLine($hGfxCtxt, $aCoordinates[$i - 1][0], $aCoordinates[$i - 1][1], $aCoordinates[0][0], $aCoordinates[0][1], $hPen)

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

    If $iRound Then _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hDC_Backbuffer, 0, 0, $SRCCOPY) ;copy backbuffer to screen (GUI)
    $iAngle -= 0.5 ;decrease overall angle
    $iRound += 1
    Until Not Sleep(30) ;Sleep() always returns 1 and Not 1 is 0 correspond to False

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

    _Exit()
    EndFunc ;==>Example

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

    Func _Exit() ;cleanup GDI+ resources
    _GDIPlus_PenDispose($hPen)
    _WinAPI_SelectObject($hDC_Backbuffer, $DC_Obj)
    _GDIPlus_GraphicsDispose($hGfxCtxt)
    _WinAPI_DeleteObject($hHBITMAP)
    _WinAPI_ReleaseDC($hGUI, $hDC)
    GUIDelete($hGUI)
    Exit
    EndFunc ;==>_Exit

    [/autoit]
  • ich hab es jetzt irgendwie hingekriegt, aber das erfüllt letzendlich irgendwie auch nicht so ganz das was ich wollte :( jetzt hab ich nämlich in der Bild-Datei nur den sichtbaren Teil des Graphen in meiner GUI.

    Werd ich wohl doch mein Script noch umschreiben und die Achs-Bezeichnungen per GDI+ erzeugen müssen. War zumindest ein Versuch...

  • Flitzpiepe84,
    dein Script aus dem Startpost ist ja mal garnicht so verkehrt, dir fehlt nur die eine Zeile, bei der du vom DC zu einer HBITMAP kommst...
    Im Prinzip musst du dir zunächst eine "leere" Bitmap erzeugen und die Bilddaten dort hinein "blitten". Diese Bitmap kannst du dann auch speichern.

    Spoiler anzeigen
    [autoit]

    #include <GuiConstantsEx.au3>
    #include <GDIPlus.au3>
    #include <ScreenCapture.au3>

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

    ; Initialize GDI+ library
    _GDIPlus_Startup()

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

    $hgui = GUICreate("test", 500, 500)
    $button = GUICtrlCreateButton("testbuttton", 30, 40, 200, 40)
    GUISetState()

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

    $DC_gui = _WinAPI_GetWindowDC($hgui) ;DC aus GUI holen

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

    $hbmp = _WinAPI_CreateCompatibleBitmap($DC_gui, 500, 500) ;leere bitmap erzeugen
    $DC_neu = _WinAPI_CreateCompatibleDC($DC_gui) ;DC neu
    _WinAPI_SelectObject($DC_neu, $hbmp) ;zuweisen

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

    While GUIGetMsg() <> -3
    WEnd
    _WinAPI_BitBlt($DC_neu, 0, 0, 500, 500, $DC_gui, 0, 0, 0x00CC0020) ;GUI in NEU blitten, ggf Ausschnitt festlegen
    $DC_desktop = _WinAPI_GetWindowDC(0) ;desktop
    _WinAPI_BitBlt($DC_neu, 50, 120, 200, 200, $DC_desktop, 0, 0, 0x00CC0020) ;DESKTOP in NEU blitten, ggf Ausschnitt festlegen

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

    $hbitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbmp) ;get bitmap
    $hBuffer = _GDIPlus_ImageGetGraphicsContext($hbitmap) ;vorbereiten fürs zeichnen
    ;draw somethinginto context
    $hPen = _GDIPlus_PenCreate(0xFFFE0F00) ;rot
    _GDIPlus_GraphicsDrawLine($hBuffer, 10, 10, 500, 500, $hPen);linie
    _GDIPlus_GraphicsDrawRect($hBuffer, 100, 100, 400, 300, $hPen);rechteck
    _GDIPlus_GraphicsDrawEllipse($hBuffer, 130, 100, 140, 70, $hPen);ellipse

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

    ; Save bitmap to file
    _GDIPlus_ImageSaveToFile($hbitmap, @ScriptDir & "\screenshot_line.bmp")

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

    ; Clean up resources
    _GDIPlus_BitmapDispose($hbitmap)

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

    ; Shut down GDI+ library
    _GDIPlus_Shutdown()

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

    ShellExecute("screenshot_line.bmp")

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


    Das geht alles natürlich noch einfacher, wenn du nicht unbedingt auf das GDI+-Gedöns stehst...

  • bei mir sieht das Script so aus:

    [autoit]


    Local $hDC = _WinAPI_GetDC($GUI_Graph)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $memBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $GraphSizeX, $GraphSizeY)
    Local $hObj = _WinAPI_SelectObject($hMemDC, $memBitmap)
    _WinAPI_BitBlt($hMemDC, 0, 0, $GraphSizeX, $GraphSizeY, $hDC, 0, 0, $SRCCOPY)

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

    $hBmp = _GDIPlus_BitmapCreateFromHBITMAP($memBitmap)
    _GDIPlus_ImageSaveToFile($hBmp, @ScriptDir &"\test.jpg")
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_DeleteObject($memBitmap)
    _WinAPI_DeleteDC($hDC)

    [/autoit]

    Also machen tut es was es soll, nur nicht so wie ich es mir vorgestellt habe :(

    Wenn ich das jetzt richtig verstehe, erfasse ich hiermit nur den Bereich der auch auf meinem Monitor sichtbar ist. Und das nutzt mir nicht wirklich was, da ich gern das gesamte Diagramm abspeichern möchte. Die Client-GUI sieht so aus, wie im Anhang(screen.jpg). Das Bild was dabei abgespeichert wird, siehe test.jpg. Der schwarze Bereich wäre dann die eigentliche Größe vom Diagramm. Vielleicht hat ja jemand noch eine Idee...

  • Hi,
    per bitblt() kannst du bis zu 16384x16384 Pixel große Bereiche blitten.
    Wenn ich das jetzt richtig verstanden habe, willst du sowohl den per GDI erzeugten Graphen als auch die Achsbeschriftungen in EIN Bild plotten?!
    Wo kommt denn das Bild her, welches in deiner GUI angezeigt wird? Dort werden doch auch die Graphen gezeichnet incl. Achsenbeschriftungen. Wieso gibst du nicht einfach genau das in eine Bilddatei aus?

  • Moin,

    habe nichts zum Testen, aber damit solltest Du ein wenig herumspielen ...

    [autoit]


    Global Const $WM_PRINT = 0x0317
    Global Const $WM_PRINTCLIENT = 0x0318

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

    Global Const $PRF_CHECKVISIBLE = 0x00000001
    Global Const $PRF_NONCLIENT = 0x00000002
    Global Const $PRF_CLIENT = 0x00000004
    Global Const $PRF_ERASEBKGND = 0x00000008
    Global Const $PRF_CHILDREN = 0x00000010
    Global Const $PRF_OWNED = 0x00000020

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

    ; ...

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

    Local $hDC = _WinAPI_GetDC($GUI_Graph)
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
    Local $memBitmap = _WinAPI_CreateCompatibleBitmap($hDC, $GraphSizeX, $GraphSizeY)
    _WinAPI_ReleaseDC($hDC) ; richtig
    Local $hObj = _WinAPI_SelectObject($hMemDC, $memBitmap)
    ;~ _WinAPI_BitBlt($hMemDC, 0, 0, $GraphSizeX, $GraphSizeY, $hDC, 0, 0, $SRCCOPY)

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

    _SendMessage ($GUI_Graph, $WM_PRINT, $hMemDC, $PRF_CLIENT)

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

    $hBmp = _GDIPlus_BitmapCreateFromHBITMAP($memBitmap)
    _GDIPlus_ImageSaveToFile($hBmp, @ScriptDir &"\test.jpg")
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_DeleteObject($memBitmap)
    ;~ _WinAPI_DeleteDC($hDC) fehler!

    [/autoit]


    Edit: Nachrichtenkonstanten nachgetragen ...
    Edit 2: Fehlerbereinigung
    Edit 3: ...


    4 Mal editiert, zuletzt von Greenhorn (8. Februar 2014 um 23:41)

  • Hi,
    per bitblt() kannst du bis zu 16384x16384 Pixel große Bereiche blitten.
    Wenn ich das jetzt richtig verstanden habe, willst du sowohl den per GDI erzeugten Graphen als auch die Achsbeschriftungen in EIN Bild plotten?!
    Wo kommt denn das Bild her, welches in deiner GUI angezeigt wird? Dort werden doch auch die Graphen gezeichnet incl. Achsenbeschriftungen. Wieso gibst du nicht einfach genau das in eine Bilddatei aus?

    der Graph wird per GDI+ erzeugt und die Achsbeschriftung per Label. Da ich aber bisher nichts gefunden habe, um ein Label in eine Bilddatei zu plotten, habe ich nach Alternativ-Lösungen gesucht. Das Bild selbst wird im Buffer erzeugt und dann angezeigt. Grundlage dafür ist die MultiGraph-UDF von "sBond" welche ich mir noch ein wenig modifziert und angepasst habe.

  • Ich nehme an, die Labels werden erst sichtbar, wenn du im Child_GUI ganz nach unten scrollst!?
    Wenn du von $hDC auf $hMemDC blitten willst, hast du folgendes Problem:
    Der Bildspeicher von $hDC ist nur so groß, wie der sichtbrae Bereich selber; Deshalb wird diese Methode nicht funktionieren!

    Es fallen mir nun 3 Möglichkeiten ein:

    1) Blitte auf mehrere Bitmaps, jeweils einen anderen Scrollbereich und setzte die Bilder danach zusammen (würd ich nicht empfehlen)

    2) Erstelle ein Child-GUI im Scroll-GUI. Dieses Fenster muss bereits die richtige Größe haben.
    Also Graph + Labels auf Child-GUI -> dieses wird teilweise angezeigt im Scroll-GUI -> dieses liegt auf Main_GUI.
    Dann sollte das Blitten funktionieren.

    3) verzichte auf Labels und beschrifte gleich mit GDIPlus! (das wäre meiner Ansicht nach die einfachste und "schönste" Variante)

    E

  • Variante 1 würde mir persönlich auch nicht wirklich gefallen. Auch schon deshalb nicht weil ich rein von den Kenntnissen gar nicht so recht wüsste wie ich das anstellen soll. War schon ein ziemlicher Krampf überhaupt _winapi_bitblt umzusetzen, geschweige denn, dass ich es momentan vollständig verstanden habe wie es funktioniert.

    Variante 2 ist so ähnlich schon umgesetzt. Der Graph wird in einer Child-GUI erzeugt welche die Scrollbar hat. <-- wenn ich dich jetzt richtig verstehe, soll ich in diese Child-GUI eine weitere GUI legen, welche dann den Graphen enthält? Muss ich ausprobieren und klingt irgendwie nach einer brauchbaren Lösung...

    Variante 3 wäre dann wohl die letzte Möglichkeit, wenn gar nichts geht.

  • Hi,
    ich würde das so machen, wie es seit Urzeiten bei jedem side- oder up/down-scrollbarem Computerspiel (und nicht nur da) gemacht wird.
    Erstelle EINE Bitmap maximaler Größe und zeige per bitblt() in der GUI nur den aktuellen Auschnitt an.
    Bei Verwendung von stretchblt() ist das Ganze dann nicht nur scroll- sondern auch zoombar.
    Die Daten werden in den Buffer geschrieben, und per bitblt() in der GUI angezeigt.
    Wenn man es genau sieht, gibt es eine "Anzeige-GUI", und eine "Daten-Bitmap".
    In die Anzeige-GUI wird ausschliesslich der gerade sichtbare Bereich geblittet. Inclusive aller grafischen Elemente, Labels usw.

  • Probiere es mal damit:

    [autoit]


    Func CaptureWindowsContent($hWnd, $sFile = @ScriptDir & "\Test.png")
    Local $aWin = WinGetPos(HWnd($hWnd))
    Local Const $iYCaption = _WinAPI_GetSystemMetrics(4)
    Local Const $iYFixedFrame = _WinAPI_GetSystemMetrics(8)
    Local Const $iXFixedFrame = _WinAPI_GetSystemMetrics(7)
    Local $aWin2 = WinGetClientSize(HWnd($hWnd))
    Local $hDC_Capture = _WinAPI_GetDC(HWnd($hWnd))
    Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC_Capture)
    Local $hHBitmap = _WinAPI_CreateCompatibleBitmap($hDC_Capture, $aWin[2], $aWin[3])
    Local $hObjectOld = _WinAPI_SelectObject($hMemDC, $hHBitmap)
    DllCall("gdi32.dll", "int", "SetStretchBltMode", "hwnd", $hDC_Capture, "uint", 4)
    DllCall("user32.dll", "int", "PrintWindow", "hwnd", $hWnd, "handle", $hMemDC, "int", 0)
    _WinAPI_DeleteDC($hMemDC)
    _WinAPI_SelectObject($hMemDC, $hObjectOld)
    _WinAPI_ReleaseDC($hWnd, $hDC_Capture)
    Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap)
    Local $hBitmap_Crop = _GDIPlus_BitmapCloneArea($hBitmap, $iXFixedFrame, $iYCaption + $iXFixedFrame, $aWin2[0], $aWin2[1])
    _GDIPlus_ImageSaveToFile($hBitmap_Crop, $sFile)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_BitmapDispose($hBitmap_Crop)
    _WinAPI_DeleteObject($hHBitmap)
    EndFunc

    [/autoit]


    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯