Jump to content
Sign in to follow this  
junkew

DllStructGetData breaks with _GDIPlus_BitmapLockBits

Recommended Posts

junkew

Why does code below break?

The difference is in first saving the screen to a file.BMP works correctly

When getting the BMP directly with lockbits things seem to be breaking when abs($Stride) * $Height) > 816

It looks like I am getting some kind of an overflow and autoit fully breaks down with an exception

Breaks on

$pixelData = DllStructCreate("byte[" & (abs($Stride) * $Height) & "]", $Scan0)

consolewrite("4 so far so good" & @CRLF)

$BMPDataStart = DllStructGetData($pixeldata,1)

consolewrite("5 so far NOT so good")

Full code to demonstrate

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

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

; Initialize GDI+ library
_GDIPlus_Startup ()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,false)

testit()


func testIt()
    Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0;

; Load the bitmap to search in
    getImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

; Load the bitmap to search in
    getImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc 

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride)
; Load the bitmap to search in
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("")
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
    EndIf
    consolewrite("so far so good")
    
;Get $tagGDIPBITMAPDATA structure
    $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32RGB)
    If @ERROR Then MsgBox(0,"","Error locking region")
    consolewrite("2 so far so good" & @CRLF)
    
    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "PixelFormat")  ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:    " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & $PixelFormat & @CRLF)
    ConsoleWrite("Bitmap Scan0:    " & $Scan0 & @CRLF)
    
    consolewrite("3 so far so good" & @CRLF)
    
    $pixelData = DllStructCreate("byte[" & (abs($Stride) * $Height) & "]", $Scan0)  
    consolewrite("4 so far so good" & @CRLF)
    $BMPDataStart = DllStructGetData($pixeldata,1)
    
    consolewrite("5 so far NOT so good")
    
    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose ($pBitmap)
    _WinAPI_DeleteObject ($pBitmap)

EndFunc  ;==>GetImage

Share this post


Link to post
Share on other sites
rover

Why does code below break?

The difference is in first saving the screen to a file.BMP works correctly

When getting the BMP directly with lockbits things seem to be breaking when abs($Stride) * $Height) > 816

It looks like I am getting some kind of an overflow and autoit fully breaks down with an exception

Breaks on

$pixelData = DllStructCreate("byte[" & (abs($Stride) * $Height) & "]", $Scan0)

consolewrite("4 so far so good" & @CRLF)

$BMPDataStart = DllStructGetData($pixeldata,1)

consolewrite("5 so far NOT so good")

Full code to demonstrate

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

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

; Initialize GDI+ library
_GDIPlus_Startup ()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,false)

testit()


func testIt()
    Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0;

; Load the bitmap to search in
    getImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

; Load the bitmap to search in
    getImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc 

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride)
; Load the bitmap to search in
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("")
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
    EndIf
    consolewrite("so far so good")
    
;Get $tagGDIPBITMAPDATA structure
    $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32RGB)
    If @ERROR Then MsgBox(0,"","Error locking region")
    consolewrite("2 so far so good" & @CRLF)
    
    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "PixelFormat") ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:    " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & $PixelFormat & @CRLF)
    ConsoleWrite("Bitmap Scan0:    " & $Scan0 & @CRLF)
    
    consolewrite("3 so far so good" & @CRLF)
    
    $pixelData = DllStructCreate("byte[" & (abs($Stride) * $Height) & "]", $Scan0)  
    consolewrite("4 so far so good" & @CRLF)
    $BMPDataStart = DllStructGetData($pixeldata,1)
    
    consolewrite("5 so far NOT so good")
    
    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose ($pBitmap)
    _WinAPI_DeleteObject ($pBitmap)

EndFunc ;==>GetImage
@junkew

see this thread:

http://www.autoitscript.com/forum/index.php?showtopic=76530

it helps to search for 'BitmapLockBits' ;)

Try this reworking of your example

#include <GDIPlus.au3>
#Include <ScreenCapture.au3>
#include <GUIConstantsEX.au3>
#include <Constants.au3>
#include <WindowsConstants.au3>

Global $pBitmap, $hGraphics
Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

$hwnd = GUICreate("_GDIPlus_BitmapLockBits() Test", 800, 600,-1,-1,-1,$WS_EX_TOPMOST)
GUISetState()

; Initialize GDI+ library
_GDIPlus_Startup ()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename,0,0,-1,-1,false)

testit()

While 1
    $msg = GUIGetMsg()
    If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd

_GDIPlus_GraphicsDispose($hGraphics)
_WinAPI_DeleteObject ($pBitmap)
Exit

func testIt()
    Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0;


; Load the bitmap to search in
    getImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

; Load the bitmap to search in
    getImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc    

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride)
; Load the bitmap to search in
Local $hbScreen
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("")
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen)
        ConsoleWrite("SCREEN" & @CRLF)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
        ConsoleWrite("FILE" & @CRLF)
    EndIf
    
    ; Show pixel format for screen capture or file
    $aRet = _GDIPlus_ImageGetPixelFormat($pBitmap)
    ConsoleWrite("Image pixel format: " & $aRet[1] & @CRLF);
    ConsoleWrite("Image pixel format constant: " & $aRet[0] & @CRLF & @CRLF);
    
    ProgressOn("Image Search", "The image is being processed.", "0 percent", -1, -1, 16)
    
;Get $tagGDIPBITMAPDATA structure
    $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _
    _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32RGB)
    If @ERROR Then MsgBox(0,"","Error locking region")
    
    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap.
;If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
;dataname is Format, not Pixelformat
    $PixelFormat = DllStructGetData($BitmapData, "Format")  ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.
    
;$Scan0 not pointer to byte array
    For $row = 0 To $Height - 1
        For $col = 0 To $Width - 1
            $pixel = DllStructCreate("dword", $Scan0 + $row * $Stride + $col*4)
            ;ConsoleWrite(Hex(DllStructGetData($pixel, 1)) & @CRLF)
        Next
        ProgressSet(Int(100 * $row / ($Height)), Int(100 * $row / ($Height)) & " Percent" & @CRLF & "Data: " & Hex(DllStructGetData($pixel, 1)))
        ;ConsoleWrite("-------" & @CRLF)
    Next

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:       " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & $PixelFormat & @CRLF)
    ConsoleWrite("Bitmap Scan0:       " & $Scan0 & @CRLF & @CRLF)
    
    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    $hgraphics = _GDIPlus_GraphicsCreateFromHWND($hwnd)
    _GDIPlus_GraphicsDrawImageRect($hgraphics, $pBitmap, 0, 0, $Width, $Height)
    ;_WinAPI_DeleteObject ($pBitmap)
    If $hbScreen Then _WinAPI_DeleteObject($hbScreen)
    ProgressOff()

EndFunc  ;==>GetImage

Func _GDIPlus_ImageGetPixelFormat($hImage)
    ;Returns pixel format of an image
    ;http://www.autoitscript.com/forum/index.php?showtopic=77741&hl=gdi
    Local $aResult, $aFormat[2] = [0, ""], $iError = 0
    If ($hImage = -1) Or (Not $hImage) Then Return SetError(4, 1, $aFormat)
    Local $aPixelFormat[14][2] = _
            [["1 Bpp Indexed", $GDIP_PXF01INDEXED], _
            ["4 Bpp Indexed", $GDIP_PXF04INDEXED], _
            ["8 Bpp Indexed", $GDIP_PXF08INDEXED], _
            ["16 Bpp Grayscale", $GDIP_PXF16GRAYSCALE], _
            ["16 Bpp RGB 555", $GDIP_PXF16RGB555], _
            ["16 Bpp RGB 565", $GDIP_PXF16RGB565], _
            ["16 Bpp ARGB 1555", $GDIP_PXF16ARGB1555], _
            ["24 Bpp RGB", $GDIP_PXF24RGB], _
            ["32 Bpp RGB", $GDIP_PXF32RGB], _
            ["32 Bpp ARGB", $GDIP_PXF32ARGB], _
            ["32 Bpp PARGB", $GDIP_PXF32PARGB], _
            ["48 Bpp RGB", $GDIP_PXF48RGB], _
            ["64 Bpp ARGB", $GDIP_PXF64ARGB], _
            ["64 Bpp PARGB", $GDIP_PXF64PARGB]]
    $aResult = DllCall($ghGDIPDll, "int", "GdipGetImagePixelFormat", "hwnd", $hImage, "int*", 0)
    $iError = @error
    If @error Or IsArray($aResult) = 0 Then Return SetError($iError, 2, $aFormat)
    For $i = 0 To 13

        If $aPixelFormat[$i][1] = $aResult[2] Then
            $aFormat[0] = $aPixelFormat[$i][1]
            $aFormat[1] = $aPixelFormat[$i][0]
            Return SetError($aResult[0], 0, $aFormat)
        EndIf
    Next
    Return SetError($aResult[0], 3, $aFormat)
EndFunc   ;==>_GDIPlus_ImageGetPixelFormat

I see fascists...

Share this post


Link to post
Share on other sites
junkew

I actually have seen that thread and searched the forum before posting the problem.

The code I made is 95% based on the thread but the only difference is that I want to get all bytes at once and not one by one with a DWORD iteration.

Based on the description there is no reason why the code should not work. It actually works when I load from bitmap files and it only doesn't work when I don't write the screenshot to a file first.

Any help?

Share this post


Link to post
Share on other sites
Malkey

I actually have seen that thread and searched the forum before posting the problem.

The code I made is 95% based on the thread but the only difference is that I want to get all bytes at once and not one by one with a DWORD iteration.

Based on the description there is no reason why the code should not work. It actually works when I load from bitmap files and it only doesn't work when I don't write the screenshot to a file first.

Any help?

This is as far as I got. I've given up for the night. I don't think it is 100% right. It may help you along, maybe.

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

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

; Initialize GDI+ library
_GDIPlus_Startup ()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename,0,0,@DesktopWidth/4,@DesktopHeight/4,false)

testit()


func testIt()
    Dim $BMP1Data="", $BMP1Width=0, $BMP1Height=0, $BMP1LineWidth=0;

; Load the bitmap to search in
    getImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

; Load the bitmap to search in
    getImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc    

Func GetImage($BMPFile, byref $BMPDataStart, byref $Width, byRef $Height, byref $Stride)
; Load the bitmap to search in
    If $BMPFile="SCREEN" Then
        $hbScreen=_ScreenCapture_Capture("",0,0,@DesktopWidth/4,@DesktopHeight/4)
        $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
    EndIf
    consolewrite("so far so good")
    
;Get $tagGDIPBITMAPDATA structure
    $BitmapData= _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32RGB)
    If @ERROR Then MsgBox(0,"","Error locking region")
    consolewrite("2 so far so good" & @CRLF)
    
    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "PixelFormat")  ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:       " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & $PixelFormat & @CRLF)
    ConsoleWrite("Bitmap Scan0:       " & $Scan0 & @CRLF)
    
    consolewrite("3 so far so good" & @CRLF)
    
    $pixelData = DllStructCreate("byte array[" & abs($Stride) * $Height*$Width*4 & "]", $Scan0)    
    consolewrite("4 so far so good" & @CRLF)
    ;$BMPDataStart = DllStructGetData($pixeldata,1)
    
    for $x = 64 to 1 step -1    
        consolewrite(Hex(DllStructGetData($pixelData, 1, $x),2) )
        if Mod($x,4) =0 then consolewrite(@CRLF)
    next
    consolewrite("5 so far NOT so good")
    
    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose ($pBitmap)
    _WinAPI_DeleteObject ($pBitmap)

EndFunc  ;==>GetImage

The color format maybe 0xRRGGBBAA. I was expecting 0xAARRGGBB format. I don't Know.

Share this post


Link to post
Share on other sites
junkew

Somehow it seems to be breaking on the DllStructGetData and seems not able to get the bytes.

Is there any limitation in referencing to byte in a struct or am I using DllStructCreate in an incorrect manner

$pixel = DllStructCreate("byte data[5242880]", $Scan0) ; Doesn't help (1280 * 1024 = 5242880

consolewrite("Struct size " & (abs($Stride) * $Height) & ":" & DllStructGetSize ( $pixel) & @crlf)

Struct size is correctly allocated

consolewrite("NOT broken")

It just breaks on this line only when (abs($Stride) * $Height) > nnn and only when getting bits from screen directly instead from file

$BMPDataStart = DllStructGetData($pixel,"data")

consolewrite("Broken code not reached")

Just found out that the 816 is not static yesterday it was 817 when it breaks

today it works till value of 9184. Could it be that there is a special value on my screen that turns things into breaking????!!!!

Just a weird problem I cannot explain and find a solution for sofar.

Share this post


Link to post
Share on other sites
rover

I actually have seen that thread and searched the forum before posting the problem.

The code I made is 95% based on the thread but the only difference is that I want to get all bytes at once and not one by one with a DWORD iteration.

Based on the description there is no reason why the code should not work. It actually works when I load from bitmap files and it only doesn't work when I don't write the screenshot to a file first.

Any help?

my apologies junkew, sometimes I don't see the trees for the forest...

I thought $Scan0 was pointer to array of pointers?, unclear on this area myself.

or max size of byte array of struct was exceeded.

anyway the screencapture is a 32bit (8 alpha bits unused) memory bitmap,

cloning the image with a 24 bitmap header works

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

; GDI+ Image File Format Constants
; Globally Unique Identifier (GUID)
Global Const $GDIP_IMAGEFORMAT_UNDEFINED = "{B96B3CA9-0728-11D3-9D7B-0000F81EF32E}" ; Windows GDI+ is unable to determine the format.
Global Const $GDIP_IMAGEFORMAT_MEMORYBMP = "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}" ; Image was constructed from a memory bitmap.
Global Const $GDIP_IMAGEFORMAT_BMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}" ; Microsoft Windowsbitmap (BMP) format.
Global Const $GDIP_IMAGEFORMAT_EMF = "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}" ; Enhanced Metafile (EMF) format.
Global Const $GDIP_IMAGEFORMAT_WMF = "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}" ; Windows Metafile Format (WMF) format.
Global Const $GDIP_IMAGEFORMAT_JPEG = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}" ; JPEG format.
Global Const $GDIP_IMAGEFORMAT_PNG = "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}" ; Portable Network Graphics (PNG) format.
Global Const $GDIP_IMAGEFORMAT_GIF = "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}" ; Graphics Interchange Format (GIF) format.
Global Const $GDIP_IMAGEFORMAT_TIFF = "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}" ; Tagged Image File Format (TIFF) format.
Global Const $GDIP_IMAGEFORMAT_EXIF = "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}" ; Exif (Exchangeable Image File) format.
Global Const $GDIP_IMAGEFORMAT_ICON = "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}" ; Icon format.

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

; Initialize GDI+ library
_GDIPlus_Startup()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename, 0, 0, -1, -1, False)

testIt()

Func testIt()
    Dim $BMP1Data = "", $BMP1Width = 0, $BMP1Height = 0, $BMP1LineWidth = 0;

    ; Load the bitmap to search in
    GetImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

    ; Load the bitmap to search in
    GetImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc   ;==>testIt

Func GetImage($BMPFile, ByRef $BMPDataStart, ByRef $Width, ByRef $Height, ByRef $Stride)
    ; Load the bitmap to search in
    If $BMPFile = "SCREEN" Then
        $hbScreen = _ScreenCapture_Capture("")
        $pBitmapCap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen) ; returns memory bitmap
        ConsoleWrite(@CRLF & '-_GDIPlus_ImageGetRawFormat($pBitmap) = ' & _GDIPlus_ImageGetRawFormat($pBitmapCap) & @CRLF)
        ; Show pixel format for screen capture
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmapCap)
        ConsoleWrite("$pBitmapCap pixel format: " & $aRet[1] & @CRLF)
        $pBitmap = _GDIPlus_BitmapCloneArea($pBitmapCap, 0, 0, _GDIPlus_ImageGetWidth($pBitmapCap), _
                _GDIPlus_ImageGetHeight($pBitmapCap), $GDIP_PXF24RGB)
        _WinAPI_DeleteObject($pBitmapCap)
        ; Show pixel format for screen capture or file
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmap)
        ConsoleWrite("$hClone pixel format: " & $aRet[1] & @CRLF)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
        ConsoleWrite('-_GDIPlus_ImageGetRawFormat($pBitmap) = ' & _GDIPlus_ImageGetRawFormat($pBitmap) & @CRLF)
        ; Show pixel format for screen capture or file
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmap)
        ConsoleWrite("Image pixel format: " & $aRet[1] & @CRLF);
    EndIf

    ;Get $tagGDIPBITMAPDATA structure
    $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _
            _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF24RGB)
    If @error Then MsgBox(0, "", "Error locking region")

    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "Format") ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:       " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & $PixelFormat & @CRLF)
    ConsoleWrite("Bitmap Scan0:       " & $Scan0 & @CRLF)

    $pixelData = DllStructCreate("byte[" & (Abs($Stride) * $Height) & "]", $Scan0)
    $BMPDataStart = DllStructGetData($pixelData, 1)
    
    ConsoleWrite('+BinaryLen($BMPDataStart) = ' & BinaryLen($BMPDataStart) & @CRLF & @CRLF)

    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose($pBitmap)
    _WinAPI_DeleteObject($pBitmap)

EndFunc   ;==>GetImage


Func _GDIPlus_ImageGetRawFormat($hImage)
    ;Returns file format GUID string and image type identifier string
    Local $aResult1, $aResult2, $tStruc, $iError = 0
    If ($hImage = -1) Or (Not $hImage) Then Return SetError(4, 1, "")
    Local $aImageType[11][2] = _
            [["UNDEFINED", $GDIP_IMAGEFORMAT_UNDEFINED], _
            ["MEMORYBMP", $GDIP_IMAGEFORMAT_MEMORYBMP], _
            ["BMP", $GDIP_IMAGEFORMAT_BMP], _
            ["EMF", $GDIP_IMAGEFORMAT_EMF], _
            ["WMF", $GDIP_IMAGEFORMAT_WMF], _
            ["JPEG", $GDIP_IMAGEFORMAT_JPEG], _
            ["PNG", $GDIP_IMAGEFORMAT_PNG], _
            ["GIF", $GDIP_IMAGEFORMAT_GIF], _
            ["TIFF", $GDIP_IMAGEFORMAT_TIFF], _
            ["EXIF", $GDIP_IMAGEFORMAT_EXIF], _
            ["ICON", $GDIP_IMAGEFORMAT_ICON]]
    $tStruc = DllStructCreate("byte[16]")
    $iError = @error
    If @error Or (Not IsDllStruct($tStruc)) Then Return SetError($iError, 2, "")
    $aResult1 = DllCall($ghGDIPDll, "int", "GdipGetImageRawFormat", "hwnd", $hImage, _
            "ptr", DllStructGetPtr($tStruc))
    $iError = @error
    If @error Or (Not IsArray($aResult1)) Or (Not IsPtr($aResult1[2])) Or _
            (Not $aResult1[2]) Then Return SetError($iError, 3, "")
    $aResult2 = DllCall("Ole32.dll", "int", "StringFromGUID2", "ptr", $aResult1[2], "wstr", "", "int", 40)
    $iError = @error
    If @error Or (Not IsArray($aResult2)) Or (Not $aResult2[2]) Then Return SetError($iError, 4, "")
    For $i = 0 To 10
        If $aImageType[$i][1] == $aResult2[2] Then
            Return SetError($aResult1[0], 0, $aImageType[$i][0])
        EndIf
    Next
    Return SetError($aResult2[0], 5, "")
EndFunc   ;==>_GDIPlus_ImageGetRawFormat

Func _GDIPlus_ImageGetPixelFormat($hImage)
    ;Returns pixel format of an image
    ;http://www.autoitscript.com/forum/index.php?showtopic=77741&hl=gdi
    Local $aResult, $aFormat[2] = [0, ""], $iError = 0
    If ($hImage = -1) Or (Not $hImage) Then Return SetError(4, 1, $aFormat)
    Local $aPixelFormat[14][2] = _
            [["1 Bpp Indexed", $GDIP_PXF01INDEXED], _
            ["4 Bpp Indexed", $GDIP_PXF04INDEXED], _
            ["8 Bpp Indexed", $GDIP_PXF08INDEXED], _
            ["16 Bpp Grayscale", $GDIP_PXF16GRAYSCALE], _
            ["16 Bpp RGB 555", $GDIP_PXF16RGB555], _
            ["16 Bpp RGB 565", $GDIP_PXF16RGB565], _
            ["16 Bpp ARGB 1555", $GDIP_PXF16ARGB1555], _
            ["24 Bpp RGB", $GDIP_PXF24RGB], _
            ["32 Bpp RGB", $GDIP_PXF32RGB], _
            ["32 Bpp ARGB", $GDIP_PXF32ARGB], _
            ["32 Bpp PARGB", $GDIP_PXF32PARGB], _
            ["48 Bpp RGB", $GDIP_PXF48RGB], _
            ["64 Bpp ARGB", $GDIP_PXF64ARGB], _
            ["64 Bpp PARGB", $GDIP_PXF64PARGB]]
    $aResult = DllCall($ghGDIPDll, "int", "GdipGetImagePixelFormat", "hwnd", $hImage, "int*", 0)
    $iError = @error
    If @error Or IsArray($aResult) = 0 Then Return SetError($iError, 2, $aFormat)
    For $i = 0 To 13
        If $aPixelFormat[$i][1] = $aResult[2] Then
            $aFormat[0] = $aPixelFormat[$i][1]
            $aFormat[1] = $aPixelFormat[$i][0]
            Return SetError($aResult[0], 0, $aFormat)
        EndIf
    Next
    Return SetError($aResult[0], 3, $aFormat)
EndFunc   ;==>_GDIPlus_ImageGetPixelFormat

I see fascists...

Share this post


Link to post
Share on other sites
Malkey

my apologies junkew, sometimes I don't see the trees for the forest...

I thought $Scan0 was pointer to array of pointers?, unclear on this area myself.

or max size of byte array of struct was exceeded.

anyway the screencapture is a 32bit (8 alpha bits unused) memory bitmap,

cloning the image with a 24 bitmap header works

Well done rover.

I had given up trying to solve this one.

Also, the 32 bit bitmap with ARGB works.

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

; GDI+ Image File Format Constants
; Globally Unique Identifier (GUID)
Global Const $GDIP_IMAGEFORMAT_UNDEFINED = "{B96B3CA9-0728-11D3-9D7B-0000F81EF32E}" ; Windows GDI+ is unable to determine the format.
Global Const $GDIP_IMAGEFORMAT_MEMORYBMP = "{B96B3CAA-0728-11D3-9D7B-0000F81EF32E}" ; Image was constructed from a memory bitmap.
Global Const $GDIP_IMAGEFORMAT_BMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}" ; Microsoft Windowsbitmap (BMP) format.
Global Const $GDIP_IMAGEFORMAT_EMF = "{B96B3CAC-0728-11D3-9D7B-0000F81EF32E}" ; Enhanced Metafile (EMF) format.
Global Const $GDIP_IMAGEFORMAT_WMF = "{B96B3CAD-0728-11D3-9D7B-0000F81EF32E}" ; Windows Metafile Format (WMF) format.
Global Const $GDIP_IMAGEFORMAT_JPEG = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}" ; JPEG format.
Global Const $GDIP_IMAGEFORMAT_PNG = "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}" ; Portable Network Graphics (PNG) format.
Global Const $GDIP_IMAGEFORMAT_GIF = "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}" ; Graphics Interchange Format (GIF) format.
Global Const $GDIP_IMAGEFORMAT_TIFF = "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}" ; Tagged Image File Format (TIFF) format.
Global Const $GDIP_IMAGEFORMAT_EXIF = "{B96B3CB2-0728-11D3-9D7B-0000F81EF32E}" ; Exif (Exchangeable Image File) format.
Global Const $GDIP_IMAGEFORMAT_ICON = "{B96B3CB5-0728-11D3-9D7B-0000F81EF32E}" ; Icon format.

Global Const $Bitmap1Filename = @TempDir & "\FULLSCREEN.bmp"

; Initialize GDI+ library
_GDIPlus_Startup()

;Save the whole screen
_ScreenCapture_Capture($Bitmap1Filename, 0, 0, @DesktopWidth / 4, @DesktopHeight / 4, False)

testIt()

Func testIt()
    Dim $BMP1Data = "", $BMP1Width = 0, $BMP1Height = 0, $BMP1LineWidth = 0;

    ; Load the bitmap to search in
    GetImage($Bitmap1Filename, $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)

    ; Load the bitmap to search in
    GetImage("SCREEN", $BMP1Data, $BMP1Width, $BMP1Height, $BMP1LineWidth)
EndFunc   ;==>testIt

Func GetImage($BMPFile, ByRef $BMPDataStart, ByRef $Width, ByRef $Height, ByRef $Stride)
    ; Load the bitmap to search in
    If $BMPFile = "SCREEN" Then
        $hbScreen = _ScreenCapture_Capture("", 0, 0, @DesktopWidth / 4, @DesktopHeight / 4, False)
        $pBitmapCap = _GDIPlus_BitmapCreateFromHBITMAP($hbScreen) ; returns memory bitmap
        ConsoleWrite(@CRLF & '-_GDIPlus_ImageGetRawFormat($pBitmap) = ' & _GDIPlus_ImageGetRawFormat($pBitmapCap) & @CRLF)
        ; Show pixel format for screen capture
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmapCap)
        ConsoleWrite("$pBitmapCap pixel format: " & $aRet[1] & @CRLF)
        $pBitmap = _GDIPlus_BitmapCloneArea($pBitmapCap, 0, 0, _GDIPlus_ImageGetWidth($pBitmapCap), _
                _GDIPlus_ImageGetHeight($pBitmapCap), $GDIP_PXF32ARGB) ; $GDIP_PXF24RGB)
        _WinAPI_DeleteObject($pBitmapCap)
        ; Show pixel format for screen capture or file
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmap)
        ConsoleWrite("$hClone pixel format: " & $aRet[1] & @CRLF)
    Else
        $pBitmap = _GDIPlus_BitmapCreateFromFile($BMPFile)
        ConsoleWrite('-_GDIPlus_ImageGetRawFormat($pBitmap) = ' & _GDIPlus_ImageGetRawFormat($pBitmap) & @CRLF)
        ; Show pixel format for screen capture or file
        $aRet = _GDIPlus_ImageGetPixelFormat($pBitmap)
        ;ConsoleWrite("Image pixel format: " & $aRet[1] & @CRLF);
    EndIf

    ;Get $tagGDIPBITMAPDATA structure
    $BitmapData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, _GDIPlus_ImageGetWidth($pBitmap), _
            _GDIPlus_ImageGetHeight($pBitmap), $GDIP_ILMREAD, $GDIP_PXF32ARGB) ; $GDIP_PXF24RGB)
    If @error Then MsgBox(0, "", "Error locking region")

    $Stride = DllStructGetData($BitmapData, "Stride");Stride - Offset, in bytes, between consecutive scan lines of the bitmap. If the stride is positive, the bitmap is top-down. If the stride is negative, the bitmap is bottom-up.
    $Width = DllStructGetData($BitmapData, "Width");Image width - Number of pixels in one scan line of the bitmap.
    $Height = DllStructGetData($BitmapData, "Height");Image height - Number of scan lines in the bitmap.
    $PixelFormat = DllStructGetData($BitmapData, "Format") ;Pixel format - Integer that specifies the pixel format of the bitmap
    $Scan0 = DllStructGetData($BitmapData, "Scan0");Scan0 - Pointer to the first (index 0) scan line of the bitmap.

    ConsoleWrite("Bitmap Stride:      " & $Stride & @CRLF)
    ConsoleWrite("Bitmap Width:       " & $Width & @CRLF)
    ConsoleWrite("Bitmap Height:      " & $Height & @CRLF)
    ConsoleWrite("Bitmap PixelFormat: " & Hex($PixelFormat) & @CRLF)
    ConsoleWrite("Bitmap Scan0:       " & $Scan0 & @CRLF)

    $pixelData = DllStructCreate("int[" & ($Width * $Height) & "]", $Scan0)
    $BMPDataStart = DllStructGetData($pixelData, 1)

    ConsoleWrite('+BinaryLen($BMPDataStart) = ' & BinaryLen($BMPDataStart) & @CRLF & @CRLF)
    For $x = 1 To $Width * $Height
        ConsoleWrite(Hex(DllStructGetData($pixelData, 1, $x)) & " ")
        If Mod($x, $Width) = 0 Then ConsoleWrite(@CRLF)
    Next
    _GDIPlus_BitmapUnlockBits($pBitmap, $BitmapData)
    _GDIPlus_ImageDispose($pBitmap)
    _WinAPI_DeleteObject($pBitmap)

EndFunc   ;==>GetImage


Func _GDIPlus_ImageGetRawFormat($hImage)
    ;Returns file format GUID string and image type identifier string
    Local $aResult1, $aResult2, $tStruc, $iError = 0
    If ($hImage = -1) Or (Not $hImage) Then Return SetError(4, 1, "")
    Local $aImageType[11][2] = _
            [["UNDEFINED", $GDIP_IMAGEFORMAT_UNDEFINED], _
            ["MEMORYBMP", $GDIP_IMAGEFORMAT_MEMORYBMP], _
            ["BMP", $GDIP_IMAGEFORMAT_BMP], _
            ["EMF", $GDIP_IMAGEFORMAT_EMF], _
            ["WMF", $GDIP_IMAGEFORMAT_WMF], _
            ["JPEG", $GDIP_IMAGEFORMAT_JPEG], _
            ["PNG", $GDIP_IMAGEFORMAT_PNG], _
            ["GIF", $GDIP_IMAGEFORMAT_GIF], _
            ["TIFF", $GDIP_IMAGEFORMAT_TIFF], _
            ["EXIF", $GDIP_IMAGEFORMAT_EXIF], _
            ["ICON", $GDIP_IMAGEFORMAT_ICON]]
    $tStruc = DllStructCreate("byte[16]")
    $iError = @error
    If @error Or (Not IsDllStruct($tStruc)) Then Return SetError($iError, 2, "")
    $aResult1 = DllCall($ghGDIPDll, "int", "GdipGetImageRawFormat", "hwnd", $hImage, _
            "ptr", DllStructGetPtr($tStruc))
    $iError = @error
    If @error Or (Not IsArray($aResult1)) Or (Not IsPtr($aResult1[2])) Or _
            (Not $aResult1[2]) Then Return SetError($iError, 3, "")
    $aResult2 = DllCall("Ole32.dll", "int", "StringFromGUID2", "ptr", $aResult1[2], "wstr", "", "int", 40)
    $iError = @error
    If @error Or (Not IsArray($aResult2)) Or (Not $aResult2[2]) Then Return SetError($iError, 4, "")
    For $i = 0 To 10
        If $aImageType[$i][1] == $aResult2[2] Then
            Return SetError($aResult1[0], 0, $aImageType[$i][0])
        EndIf
    Next
    Return SetError($aResult2[0], 5, "")
EndFunc   ;==>_GDIPlus_ImageGetRawFormat

Func _GDIPlus_ImageGetPixelFormat($hImage)
    ;Returns pixel format of an image
    ;http://www.autoitscript.com/forum/index.php?showtopic=77741&hl=gdi
    Local $aResult, $aFormat[2] = [0, ""], $iError = 0
    If ($hImage = -1) Or (Not $hImage) Then Return SetError(4, 1, $aFormat)
    Local $aPixelFormat[14][2] = _
            [["1 Bpp Indexed", $GDIP_PXF01INDEXED], _
            ["4 Bpp Indexed", $GDIP_PXF04INDEXED], _
            ["8 Bpp Indexed", $GDIP_PXF08INDEXED], _
            ["16 Bpp Grayscale", $GDIP_PXF16GRAYSCALE], _
            ["16 Bpp RGB 555", $GDIP_PXF16RGB555], _
            ["16 Bpp RGB 565", $GDIP_PXF16RGB565], _
            ["16 Bpp ARGB 1555", $GDIP_PXF16ARGB1555], _
            ["24 Bpp RGB", $GDIP_PXF24RGB], _
            ["32 Bpp RGB", $GDIP_PXF32RGB], _
            ["32 Bpp ARGB", $GDIP_PXF32ARGB], _
            ["32 Bpp PARGB", $GDIP_PXF32PARGB], _
            ["48 Bpp RGB", $GDIP_PXF48RGB], _
            ["64 Bpp ARGB", $GDIP_PXF64ARGB], _
            ["64 Bpp PARGB", $GDIP_PXF64PARGB]]
    $aResult = DllCall($ghGDIPDll, "int", "GdipGetImagePixelFormat", "hwnd", $hImage, "int*", 0)
    $iError = @error
    If @error Or IsArray($aResult) = 0 Then Return SetError($iError, 2, $aFormat)
    For $i = 0 To 13
        If $aPixelFormat[$i][1] = $aResult[2] Then
            $aFormat[0] = $aPixelFormat[$i][1]
            $aFormat[1] = $aPixelFormat[$i][0]
            Return SetError($aResult[0], 0, $aFormat)
        EndIf
    Next
    Return SetError($aResult[0], 3, $aFormat)
EndFunc   ;==>_GDIPlus_ImageGetPixelFormat

Share this post


Link to post
Share on other sites
junkew

thx rover for the well documented solution.

I can work with the 24bits solution (although it leaves me clueless why it breaks with the PXF32RGB)

GDIPlus_BitmapCloneArea seems not needed as lockbits function takes care of it.

Unfortunately there is not much speed gain by getting the desktop directly instead of writing/reading to file (saves 100-200 milliseconds on whole desktop which is not much gain as part of whole algoritm I am working on).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×