Jump to content
Sign in to follow this  
Bruns8234

Problem with _GDIPlus_BitmapLockBits Data structure

Recommended Posts

Bruns8234

Hi!

I have a problem with a colour shift when I try to copy pixel from one bitmap to another with _GDIPlus_BitmapLockBits-Function.

Background is that I want to analyse and manipulate some bitmaps.

So I started to seach the forum and found this topic which looked interessting to me. I used there script as a base for my own modifications.

During testing I had some success, but finally I realised, that the copied pixels in the right part are slightly shifted - and I do not know whats the reason for...

This is the first time I try to code such functions until know, so I would be grateful is someone in this forum can help me.

Actual the testcode will load spectrum.bmp into left part of GUI window and Sterne.bmp into the right part.

Then every even row from the left should be copied pixel by pixel to the corresponding row in the right picture.

This is my actual code:

#include <GDIPlus.au3>
#include <Clipboard.au3>
#include <GUIConstantsEX.au3>
#include <Misc.au3>

Global Const $GUI_Width = 200
Global Const $GUI_Height = 100
$strFileName = "spectrum.bmp"
$strFileName2 = "Sterne.bmp"
$hwnd = GUICreate("GUI", 201, 100)
GUISetState()

_GDIPlus_Startup()
;~ ;Get handle to an Image object
$pBitmap = _GDIPlus_ImageLoadFromFile($strFileName)
ConsoleWrite($pBitmap & @CRLF)

$pTarget = _GDIPlus_ImageLoadFromFile($strFileName2)

; First get the hBitmap-Handle from the Clipboard
;~ $hBitmap = _ClipBoard_GetData($CF_BITMAP)

; Get a hBitmap-Handle for the clipboard-content
;~ $pBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)

; Create a Image-Object (like _GDIPlus_ImageLoadFromFile
;~ If @error Then
;~  ConsoleWrite("Error: " & @error)
;~  Exit
;~ EndIf

ConsoleWrite("Source graphic:" & @CRLF)
ConsoleWrite($pBitmap & @CRLF)
ConsoleWrite(" Width x Height: " & _GDIPlus_ImageGetWidth($pBitmap) & " x " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF)
ConsoleWrite("Target graphic:" & @CRLF)
ConsoleWrite($pTarget & @CRLF)
ConsoleWrite(" Width x Height: " & _GDIPlus_ImageGetWidth($pBitmap) & " x " & _GDIPlus_ImageGetHeight($pBitmap) & @CRLF)

;Get handle to the GUI Graphics object (the inner area of the GUI window
$graphics = _GDIPlus_GraphicsCreateFromHWND($hwnd)

;Draw the picture into the GUI - it will stretch to fit the left part of the GUI
_GDIPlus_GraphicsDrawImageRect($graphics, $pBitmap, 0, 0, 100, $GUI_Height)

;Draw the target picture into the GUI - it will strech to fit the right part of the GUI
_GDIPlus_GraphicsDrawImageRect($graphics, $pTarget, 101, 0, 100, $GUI_Height)

;Now get the Pixeldata from $pBitmap via BitmapLockBits in Readmode
$xSourceData = _GDIPlus_BitmapLockBits($pBitmap, 0, 0, 100, 100, $GDIP_ILMREAD, $GDIP_PXF32RGB)
If @ERROR Then MsgBox(0,"","Error locking region for reading")

;Also we need Pixeldata from $pTarget via BitmapLockBits in Writemode
$xTargetData = _GDIPlus_BitmapLockBits($pTarget, 0, 0, 100, 100, $GDIP_ILMWRITE, $GDIP_PXF32RGB)
If @ERROR Then MsgBox(0,"","Error locking region for writing")

; Now run in loops to copy even row from the source picture to the target picture with pixelget and pixelput (maybe its slow, but ist only for testing)
For $iRow = 0 To 99 Step 2
    For $iCol = 0 To 99
    $iColor = GetPixelFromBuffer($xSourceData, $iCol, $iRow)
    If @error Then MsgBox(0, "", "Error while reading pixel data", 1)
    SetPixelFromBuffer($xTargetData, $iCol, $iRow, $iColor)
    If @error Then MsgBox(0, "", "Error while writing pixel data", 1)
    Next
Next

;Unlock region previously locked for reading and writing
_GDIPlus_BitmapUnlockBits($pBitmap, $xSourceData)
_GDIPlus_BitmapUnlockBits($pTarget, $xTargetData)

;Finally update the target picture in the GUI window right part
_GDIPlus_GraphicsDrawImageRect($graphics, $pTarget, 101, 0, 100, 100)

; Wait until GUI is closed, then exit
While 1
    $msg = GUIGetMsg()
    If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd

; Get the Pixelcolor of Pos. $iX, $iY in $xBitmapData, where Top, Left Point is 0,0
Func GetPixelFromBuffer($xBitmapData, $iX, $iY)
    Local $iStride = DllStructGetData($xBitmapData, "Stride")
    Local $iWidth = DllStructGetData($xBitmapData, "Width")
    Local $iHeight = DllStructGetData($xBitmapData, "Height")
    Local $iPixelFormat = DllStructGetData($xBitmapData, "PixelFormat")
    Local $iOffset = DllStructGetData($xBitmapData, "Scan0")
    Local $iCoordFail = 0
    Local $iPixel, $xPixel, $iColor, $iAlpha

;~ ConsoleWrite(StringFormat("\r\nSetPixelFromBuffer(BitmapData=0x%X , X=%u , Y=%u)\r\n\tStride=%d Width=%u Height=%u PixelFormat=0x%X Offset=0x%X\r\n", $xBitmapData, $iX, $iY, $iStride, $iWidth, $iHeight, $iPixelFormat, $iOffset))

    ; Check requested coord inside BitmapData
    $iCoordFail = _Iif($iX >= $iWidth, 1, 0) + _Iif($iY >= $iHeight, 2, 0)
    If $iCoordFail > 0 Then
;~  ConsoleWrite(StringFormat("GetPixelFromBuffer returns 0, @error = -1, @extended = %u\r\n", $iCoordFail))
        ; Return 0 as Result, @Error = -1 (Coordinates out of Range) and @Extended to indicate which Coord is out of range as 1 = X-coord, 2 = Y-coord, 3=both coords)
        Return SetError(-1, $iCoordFail, 0)
    EndIf

    ; Adjust $iY if necessary (see Parameter Stride)
    If $iStride < 0 Then
        $iY = $iHeight - $iY
    EndIf

    ; Calculate Address of requested Pixel and get the Color and the Alpha channel data
    $iPixel = $iOffset + $iY * $iStride + $iX * 4
    $xPixel = DllStructCreate("BYTE alpha;BYTE color[3]", $iPixel)
    $iColor = DllStructGetData($xPixel, "color")
    $iAlpha = DllStructGetData($xPixel, "alpha")
;~  ConsoleWrite(StringFormat("GetPixelFromBuffer returns 0x%X, @error = 0, @extended = %u\r\n", $iColor, $iAlpha))
    ; Return the Result with @error = 0 and @extended indicates the alpha channel data
    Return SetError(0, $iAlpha, $iColor)
EndFunc 

; Set the Pixelcolor of Pos. $iX, $iY in $xBitmapData, where Top, Left Point is 0,0 and $iAlpha is between 0 = Full transparent and 255 = Full Opac and $iColor is in 32bit RGB-Format
Func SetPixelFromBuffer($xBitmapData, $iX, $iY, $iColor, $iAlpha = -1)
    Local $iStride = DllStructGetData($xBitmapData, "Stride")
    Local $iWidth = DllStructGetData($xBitmapData, "Width")
    Local $iHeight = DllStructGetData($xBitmapData, "Height")
    Local $iPixelFormat = DllStructGetData($xBitmapData, "PixelFormat")
    Local $iOffset = DllStructGetData($xBitmapData, "Scan0")
    Local $iCoordFail = 0
    Local $iPixel, $xPixel

;~ ConsoleWrite(StringFormat("\r\nSetPixelFromBuffer(BitmapData=0x%X , X=%u , Y=%u , Color=0x%X , Alpha=%d)\r\n\tStride=%d Width=%u Height=%u PixelFormat=0x%X Offset=0x%X\r\n", $xBitmapData, $iX, $iY, $iColor, $iAlpha, $iStride, $iWidth, $iHeight, $iPixelFormat, $iOffset))
    ; Check requested coord inside BitmapData
    $iCoordFail = _Iif($iX >= $iWidth, 1, 0) + _Iif($iY >= $iHeight, 2, 0)
    If $iCoordFail > 0 Then
;~  ConsoleWrite(StringFormat("SetPixelFromBuffer returns 0, @error = -1, @extended = %u\r\n", $iCoordFail))
        ; Return 0 as Result, @Error = -1 (Coordinates out of Range) and @Extended to indicate which Coord is out of range as 1 = X-coord, 2 = Y-coord, 3=both coords)
        Return SetError(-1, $iCoordFail, 0)
    EndIf

    ; Adjust $iY if necessary (see Parameter Stride)
    If $iStride < 0 Then
        $iY = $iHeight - $iY
    EndIf
    ; Calculate Address of requested Pixel
    $iPixel = $iOffset + $iY * $iStride + $iX * 4
    $xPixel = DllStructCreate("BYTE alpha;byte color[3]", $iPixel)
    DllStructSetData($xPixel, "color", $iColor)
    If $iAlpha > -1 Then
        DllStructSetData($xPixel, "alpha", $iColor)
    EndIf 
;~  ConsoleWrite("SetPixelFromBuffer returns 0, @error = 0, @extended = 0" & @CRLF)
    ; Return the Result
    Return SetError(0, 0, 0)
EndFunc

Func OnAutoItExit()
    If $pBitmap Then _GDIPlus_ImageDispose($pBitmap)
    If $pTarget Then _GDIPlus_ImageDispose($pTarget)
    _GDIPlus_Shutdown()
EndFunc ;==>OnAutoItExit?

For testing I have also attached the two pictures.

I think my problem has something to do with the design of the Data Structure for Pixeldata acces ($xPixel).

And there is also a second problem which I can't understand at all:

I tried to replace the first picture loaded with one from the clipboard, but whenever I tried this, there is no picture at all.

Can someone give me some help on this two problems? I have reached the end of my personal knowledge.

Thanks in advanced for any help

With best regards

Thorsten

Share this post


Link to post
Share on other sites
Makaule

As i know _GDIPlus_ImageLoadFromFile is not creating BitMap. For creating BitMap you need to use _GDIPlus_BitmapCreateFromFile. In lines 34 and 37 you are checking totaly same things, so i think you made misstake with $pBitmap and $pTarget.

If you want to replace the image you need to use _GDIPlus_ImageDispose for releasing image object before replacing image, for example: You have worked with 1 image called test.bmp so before replacing test.bmp, you need need to use _GDIPlus_ImageDispose and only then you may replace that image.

My advice for you would be to divide your project in smaller parts. Lets say first make sure you may make BitMap and then replace the file, then make other script, where you could draw small part of what you need and check is it working, then something more, just dont try to do everything at once and then get tons of error, which will be hard to understand as you dont know where it starts.

Edited by Makaule

Share this post


Link to post
Share on other sites
Bruns8234

Hi Makaule,

first of all thanks for you support. As you suggested I have divided my project in small parts which should more easy to understand and to debug... :idea:

First problem I like to solve is the handling of pictures from the clipboard...

I reduced my code to the following script, which didn't do what it is written for...

#include <GDIPlus.au3>
#include <Clipboard.au3>
#include <ScreenCapture.au3>
#include <GUIConstantsEx.au3>
#include <Misc.au3>

; Test 1 Get one grafik from a file and show this in GUI, get another grafik from clipboard and show also in the GUI

$strFileName = "spectrum.bmp"

$hwnd = GUICreate("GUI", 201, 100)
GUISetState()

_GDIPlus_Startup()

;Get handle to the GUI Graphics object (the inner area of the GUI window
$GUI_window = _GDIPlus_GraphicsCreateFromHWND($hwnd)


; +++ Step 1: Load a grafic from a file and show this in the GUI window

;Get handle to an Image object (loaded from file)
$pGraphicsFromFile = _GDIPlus_ImageLoadFromFile($strFileName)
ConsoleWrite("Graphic No.1: Width x Height: " & _GDIPlus_ImageGetWidth($pGraphicsFromFile) & " x " & _GDIPlus_ImageGetHeight($pGraphicsFromFile) & @CRLF)

;Draw the loaded grafic into the GUI - it will stretch to fit the left part of the GUI
_GDIPlus_GraphicsDrawImageRect($GUI_window, $pGraphicsFromFile, 0, 0, 100, 100)

; +++ Step 2: Load a grafic from the clipboard and show this also in the GUI window
$hClipboardContent = _ClipBoard_GetData($CF_BITMAP)                         ; This should give a HBITMAP handle
$pClipboardBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hClipboardContent)    ; and this should giva a Bitmap-Object

;Now draw the clipboard content to the right part of the GUI...
$hClipboardGraphics = _GDIPlus_ImageGetGraphicsContext($pClipboardBitmap)
_GDIPlus_GraphicsDrawImageRect($GUI_window, $hClipboardContent, 101, 0, 100, 100)

; Wait until GUI is closed, then exit
While 1
    $msg = GUIGetMsg()
    If $msg = $GUI_EVENT_CLOSE Then ExitLoop
WEnd


Func OnAutoItExit()
    _GDIPlus_GraphicsDispose($GUI_window)
    _GDIPlus_GraphicsDispose($pGraphicsFromFile)
    _GDIPlus_BitmapDispose($pClipboardBitmap)
    _GDIPlus_Shutdown()
EndFunc ;==>OnAutoItExit?

When I start this I have the picture loaded from file visible, but the picture taken from clipboard didn't. My problem is that I'm totally screwed up with HBITMAP, Bitmap-Object, Image and graphics.........

Did someone know a solution for this? I'm sure it is not sooo complicated, if you know what to do...

Thanks

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  

×

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.