Jump to content
Sign in to follow this  
evilertoaster

Bitmap Library

Recommended Posts

Nice little library... if I could get it to work...

Opt("ColorMode", 1)
Local $sz_BMP = @ScriptDir & "\Checksum_" & $cs_Current & ".bmp"
If (Not FileExists($sz_BMP)) Then
    Local $BMP = _BMPCreate(13,13)
    For $iterX = $X To $X+13
        For $iterY = $Y To $Y+13
            _PixelWrite($BMP,$X,$Y,PixelGetColor($iterX,$iterY))
        Next
    Next
    _BMPWrite($BMP,$sz_BMP)
EndIf
Opt("ColorMode", 0)

Basically, I have a PixelChecksum from earlier in my script and then if it is an unknown one, I want it to create a BMP of that area (13x13 icon) so I can look at it later. Only problem is, all the BMPs I create are all white. Help?

Thanx,

-CMR

Share this post


Link to post
Share on other sites

it takes a LOT of time to open a file from the size of the screen (like a bitmap 1024x768).

Is there a new version of your library ?

Is there any way to improve that performace ?

Thx and congratulations for the nice library !

Share this post


Link to post
Share on other sites

it takes a LOT of time to open a file from the size of the screen (like a bitmap 1024x768).

Is there a new version of your library ?

Is there any way to improve that performace ?

Thx and congratulations for the nice library !

You might try the ScreenCap module from Auto3Lib. It can capture any portion of the screen, does not require a DLL and can do a full screen capture in milliseconds.

Auto3Lib: A library of over 1200 functions for AutoIt

Share this post


Link to post
Share on other sites

You might try the ScreenCap module from Auto3Lib. It can capture any portion of the screen, does not require a DLL and can do a full screen capture in milliseconds.

The problem is not capturing. Capturing I can do fine with a DLLcall, its fast. The problem is opening and reading the bmp file saved from that screenshot.

Share this post


Link to post
Share on other sites

Humm, I havn't worked on this much at all recently. In all honesty I thought it would have become obsolete by now, but I'm glad too se it's getting some use still with the alternitves available.

I'll look at it today and see if i need to change anything since its last release.

Share this post


Link to post
Share on other sites

I've updated the _BMPWrite function to work correctly with the differnt way autoit handles binary files since it was created.

At some point now I can probably go back and make a few speed tweaks to it since I don't think I'll have to manually parse out my strings into binary data now which could help with loading and writing times.

Share this post


Link to post
Share on other sites

At some point now I can probably go back and make a few speed tweaks to it since I don't think I'll have to manually parse out my strings into binary data now which could help with loading and writing times.

Done now. See first post.

Share this post


Link to post
Share on other sites

Why in this 2.0 BMPOpen create me an array contains only the high and wight of picture ?? 1.9 was returning array with all pixels in it.

this is my code:

#include "BMP2.au3"
#include <array.au3>
global $string,$string1,$string2a,$sr1,$sr2,$sr3,$string3
$plik=""
do
$plik=FileOpenDialog("Wybierz plik",@MyDocumentsDir,"Images (*.bmp)", 1 + 2)
until $plik<>""
$BMP=_BMPOpen($plik,1)
_ArrayDisplay($BMP,"BMP")
FileDelete(@ScriptDir&"\MyBMP.bmp")
_BMPWrite($BMP,@ScriptDir & "\MyBMP.bmp");Closes the BMP to a file
GUICreate("BMP Example",_BMPGetWidth($BMP)+100, _BMPGetHeight($BMP)+100)
GUICtrlCreatePic(@ScriptDir & "\MyBMP.bmp", 58, 10, _BMPGetWidth($BMP), _BMPGetHeight($BMP))
GUISetState()
Do
    $msg = GUIGetMsg()
Until $msg = -3;$GUI_EVENT_CLOSE

How can I obtain array with all pixels ?

Edited by Uriziel01

Share this post


Link to post
Share on other sites

How can I obtain array with all pixels ?

The bitmap is stored differently. But you should not have to access the array directly...

The binary data is stored in $BMPHandle[3] if that helps... but like it said the whole point of the library is you wouldn't have to work with this data yourself.

As far as your code goes---

you have FileOpen($plik,1), but you can't do this in 2.0 becuase the progress bar 'feature' was removed. The BMP opening happens almost instantly so there's no need for a progress bar.

Share this post


Link to post
Share on other sites

Somewhere is a bug. Check this BMPs overlay example: the result picture contain some colors that isn't in nor pictures.

Download example

Edited by Andreik

When the words fail... music speaks

Share this post


Link to post
Share on other sites

I have an idea for speed improvement without any BIG rewites especially in _PixelWrite()

Princip would be in storing binary data in structure of bytes instead of one big string of binary data.

_PixelWrite():

...

$struct = DllStructCreate('byte['&BinaryLen($BMPHandle[3])&']', $BMPHandle[3])

DllStructSetData($struct, $index, $data)

...

Original is very inefficient

Func _PixelWrite(ByRef $BMPHandle,$x,$y,$color)

If $x>$BMPHandle[1]-1 Or $x<0 Or $y>$BMPHandle[2]-1 Or $y<0 or StringLen($color)<>6 or Dec($Color)=0 Then Return 0

$color = _Reverse6($Color)

$BMPHandle[3]=BinaryMid($BMPHandle[3],1,_ChordToOffset($x,$y,$BMPHandle))&Binary("0x"&$color)&BinaryMid($BMPHandle[3],_ChordToOffset($x,$y,$BMPHandle)+4)

Return 1

EndFunc

Maybe such structure can also stored in $BMPHandle[3] instead of Binary string ...

Share this post


Link to post
Share on other sites

Somewhere is a bug. Check this BMPs overlay example: the result picture contain some colors that isn't in nor pictures.

Download example

Try the new one (see first page).

Also your GBR2RGB() function probably won't be needed as it seemed to be a workaround to a larger problem. All the colors should be in RBG() format already.

And lastly I thought I'd note:

For $I = 0 To $X
   For $J = 0 To $Y

The heights and widths start at 0. For example: (0,0) is the first pixel, not (1,1) as was in your code (not that it matters in this case)

Share this post


Link to post
Share on other sites

I have an idea for speed improvement without any BIG rewites especially in _PixelWrite()

_PixelWrite():

...

$struct = DllStructCreate('byte['&BinaryLen($BMPHandle[3])&']', $BMPHandle[3])

DllStructSetData($struct, $index, $data)

Original is very inefficient

Maybe such structure can also stored in $BMPHandle[3] instead of Binary string ...

Indeed the largest problem with _PixelWrite() is it doesn't scale well with large bitmaps since it has to copy the whole thing every time a pixel is written. No question it's faster, but now that I'm back here looking... I wounder what would be faster...using DllStructSetData or creating an native array of binarystrings.

Well, something I'll probably check out sometime when I can. (or hopefully someone else does it before then :))

Share this post


Link to post
Share on other sites

I wounder what would be faster...using DllStructSetData or creating an native array of binarystrings.

The question is, what do you mean with "faster"? Arrayfunctions are slow by nature and there is no reason to use them in this case. If you have the Bitmapdata in a struct, you could use not only the DllStructSetData but also something like external dll´s (prospeed i.e.) to work with the bitmap . An other idea is to work with stringreplace instead of  $string=stringleft()+$partofstring+stringright.

Simple Example 5 times faster:

#include <Array.au3>
#include <bmp.au3>

$MAIN = GUICreate("BMP Overlay",320,135)
$PIC1 = GUICtrlCreatePic("",5,5,100,100,0x1000)
$PIC2 = GUICtrlCreatePic("",110,5,100,100,0x1000)
$PIC3 = GUICtrlCreatePic("",215,5,100,100,0x1000)
$LOAD1 = GUICtrlCreateButton("Load",5,110,100,20)
$LOAD2 = GUICtrlCreateButton("Load",110,110,100,20)
$OVERLAY = GUICtrlCreateButton("Overlay",215,110,100,20)
GUISetState()

While True
    $MSG = GUIGetMsg()
    Switch $MSG
        Case $LOAD1
            $PATH1 = FileOpenDialog("Load",@ScriptDir,"Bitmap (*.bmp)",1)
            If Not @error Then GUICtrlSetImage($PIC1,$PATH1)
        Case $LOAD2
            $PATH2 = FileOpenDialog("Load",@ScriptDir,"Bitmap (*.bmp)",1)
            If Not @error Then GUICtrlSetImage($PIC2,$PATH2)
        Case $OVERLAY
            $t=timerinit()    
            $BITMAP1 = _BMPOpen($PATH1,True)
            $BITMAP2 = _BMPOpen($PATH2,True)
            $X = _BMPGetWidth($BITMAP1)
            $Y = _BMPGetHeight($BITMAP1)
            $PROGRESS = ProgressOn("Overlay BMPs","Overlay...")
            
            For $I = 1 To $X
                For $J = 1 To $Y
                    $PIXEL = _PixelRead($BITMAP2,$I,$J)
                    If $PIXEL <> "FFFFFF" Then _PixelWrite($BITMAP1,$I,$J,GBR2RGB($PIXEL))
                    ;ProgressSet($I*100/$X,"Offset: " & $I & "x" & $J)   ; not a good position
                Next
            ;ProgressSet($I*100/$X,"Offset: " & $I & "x" & $J)  ;saves half the time and looks the same :o)
            Next
            ProgressOff()
            _BMPWrite($BITMAP1,@ScriptDir & "\mybitmap.bmp",True)
            GUICtrlSetImage($PIC3,@ScriptDir & "\mybitmap.bmp")
            
            $m=timerdiff($t)
            msgbox(0,"Time old version",$m)
            
            $t=timerinit()
            _overlay($path1,$path2)
            GUICtrlSetImage($PIC3,@ScriptDir & "\overlay.bmp")            
            $m=timerdiff($t)
                msgbox(0,"Time function version",$m)
            
        Case -3
            Exit
    EndSwitch
    Sleep(10)
WEnd

Func GBR2RGB($COLOR)
    Return StringRight($COLOR,2) & StringLeft($COLOR,2) & StringMid($COLOR,3,2)
EndFunc


func _overlay($bitmapfile1,$bitmapfile2,$ovlcolor="0xFFFFFF")  ;Color in BGR !!!
    local $data1=fileread($bitmapfile1)  ;open bitmapfiles
    local $data2=fileread($bitmapfile2)
    local $bmp2=StringTrimLeft($data2,54)  ;"raw" bitmapdata of file #2
    
    local $bmp2data=_bmpopen($bitmapfile2)  ;get width and height of bitmap
    local $width = _BMPGetWidth($bmp2data)
    local $height = _BMPGetHeight($bmp2data)
    
    local $fillbytes=$bmp2data[0]  ;number of fillbytes at end of each line
    local $ovlcol=binarytostring($ovlcolor)   ;Overlaycolor 
    
    for $i= 0 to $height-1  ;rows
        for $c=1 to $width*3 step 3 ;each pixel in the row  
        
        $pixel=stringmid($bmp2,$i*(($width*3)+$fillbytes)+$c,3)
    
        if $pixel<>$ovlcol Then  ;if pixelcolor<>overlaycolor then
                ;consolewrite($i&"  "&($c-1)/3&@crlf)
                $data1=stringreplace($data1,54+$i*(($width*3)+$fillbytes)+$c,$pixel)  ;replace pixel in the data of file #1
        endif            
        next
    next    
    $filehandle=fileopen("overlay.bmp",18)
    filewrite($filehandle,$data1)    
    fileclose($filehandle)
endfunc

And have a look at the comments of the position of the ProgressSet(). 

Andy

Edited by AndyG

Share this post


Link to post
Share on other sites

I know about ProgressSet but I put this line inside of second loop to get real value of $J. But you are right is not important because is fast.


When the words fail... music speaks

Share this post


Link to post
Share on other sites

The question is, what do you mean with "faster"?

To me, faster means taking less time to execute...

That is, would it be faster (per function call) to use the structs like Zenda mentioned or would it be faster to break the binary stream into an array of 3byte chunks so that when you write a pixel you could simply say-

$bitmap[$someOffset]=0xDDFFBB.

Arrayfunctions are slow by nature and there is no reason to use them in this case

Well, slow relative to what? stringmid (or binarymid) isn't very fast on large streams of data either. It may be the 'lesser of 2 evils' but one of them will probably be faster than the other.

but also something like external dll´s (prospeed i.e.) to work with the bitmap

The whole point of this library is to have native (no external dll) support for bitmaps. It's debatably silly to do in a scripting language that CAN use external dlls, but that's what it is.

An other idea is to work with stringreplace instead of $string=stringleft()+$partofstring+stringright

Do stringleft and stringright work on binary data? there is no BinaryLeft() or BinaryRight() so my option was only BinaryMid(). Either way, the method you describe there is really what's already being done (to my knowledge).

@Andreik: No news = good news then? (is it working?)

Share this post


Link to post
Share on other sites

evilertoaster

While playing around with your "draw lines on bitmap" example, and seeing what was possible, I discovered it is possible to draw lines on a bitmap with StringRegExpReplace.

If both BmpCreates are bumped up to 640,640 , the difference in creation times is more apparent.

I am posting this here to share as a proof of concept (or, amazed that it worked).

;
;#include <Array.au3>
;#include "BMP.au3"
; http://www.autoitscript.com/forum/index.php?showtopic=27362&view=findpost&p=742082

_SetCursor(@WindowsDir & "\cursors\hourglas.ani", 32512) ; Set cursor to hour-glass.

$BMP = _BMPCreate(64, 64)   ;Create BMP Handle with dimensions 64X64
$BMP = _BMPCreateMod(64, 64) ;Create BMP Handle with dimensions 64X64

Local $Header = StringLeft($BMP[3], 110); 110 = "0x" + 54bytes
Local $sImageData = StringTrimLeft($BMP[3], 110)

$sImageDataMod1 = StringRegExpReplace($sImageData, "(.{6})", "\1 ")

;All image to pale blue
Local $sImageDataMod2 = StringRegExpReplace($sImageDataMod1, "(.{2})(.{2})(.{2}) ", "${1}${2}D0 ")

; Red diagonal top-right to bottom-left
Local $sImageDataMod3 = StringRegExpReplace($sImageDataMod2, "((.{6} ){" & $BMP[2] & "})", "${1} 0000FF ")

;Black vertical center line.
Local $sImageDataMod4 = StringRegExpReplace($sImageDataMod3, "(.{" & (($BMP[1] / 2 - 1) * 7) & "})(.{6} )(.{" & ($BMP[1] * 7 / 2) & "})", "${1} 000000 ${3} ") ;

;Create replacement (yellow horizontal coloured) line.
Local $SlINE = StringRegExpReplace(StringFormat("%" & $BMP[1] & "s", " "), ".", "00FFFF")

;Separate each line of bitmap with space
Local $sImageDataModLines = StringRegExpReplace(StringStripWS($sImageDataMod4, 8), "(.{" & (($BMP[1]) * 6) & "})", "\1 ")

Local $aLines = StringSplit($sImageDataModLines, " "), $sImageDataMod, $sL; Split on space
;_ArrayDisplay($aLines)

$aLines[Int($aLines[0] / 2)] = $SlINE ; yellow horizontal center line.

For $i = 1 To UBound($aLines) - 1
    ;Green diagonal top-left to bottom-right
    $sL = StringRegExpReplace($aLines[$i], "(.{6})", "\1 ")
    $sImageDataMod &= StringStripWS(StringRegExpReplace($sL, "((?:.{6} ){" & (UBound($aLines) - 3 - $i) & "})(.{6} )((?:.{6} ){" & ($i - 1) & "})", "${1} 00FF00 \3"), 8)
Next

$BMP[3] = $Header & StringStripWS($sImageDataMod, 8)

_BMPWrite($BMP, @ScriptDir & "\MyBMP.bmp");Closes the BMP to a file

GUICreate("BMP Example", $BMP[1] + 20, $BMP[2] + 20)
GUICtrlCreatePic(@ScriptDir & "\MyBMP.bmp", 10, 10, $BMP[1], $BMP[2])
GUISetState()

;http://www.autoitscript.com/forum/index.php?showtopic=25898&view=findpost&p=676920
DllCall("user32.dll", "int", "SystemParametersInfo", "int", 0x57, "int", 0, "int", 0, "int", 0); Reset Cursor

Do
    $msg = GUIGetMsg()
Until $msg = -3;$GUI_EVENT_CLOSE

Func _BMPCreateMod($Width, $Height)
    $begin = TimerInit()
    Local $rowData, $imageData
    ;***BMP header (54bytes total)***
    Local $Header = "0x424D00000000000000003600000028000000" & _
            _Reverse8(Hex($Width, 8)) & _       ;4bytes, bitmap width
            _Reverse8(Hex($Height, 8)) & _  ;4bytes, bitmap hieght
            "0100180000000000" & _
            _Reverse8(Hex(($Height) * ($Width) * 3 + ($Height * Mod($Width, 4)), 8)) & _    ;4bytes, bitmap data size
            "00000000000000000000000000000000"

    $rowData = StringRegExpReplace(StringFormat("%" & $Width & "s", " "), ".", "FFFFFF")

    $imageData = StringRegExpReplace(StringFormat("%" & $Height & "s", " "), ".", $rowData)
    ;ConsoleWrite($imageData & @CRLF)
    Local $BMPHandle[4] = [Mod($Width, 4), $Width, $Height, $Header & $imageData]
    ConsoleWrite("_BMPCreateMod " & TimerDiff($begin) & @CRLF)
    Return $BMPHandle
EndFunc ;==>_BMPCreateMod

;Copied from BMP.au3 file - ADDED TIMER
Func _BMPCreate($Width, $Height)
    $begin = TimerInit()
    Local $c = Mod($Width, 4)
    Local $d = Binary("")
    If $c = 3 Then $d = Binary("0x000000")
    If $c = 2 Then $d = Binary("0x0000")
    If $c = 1 Then $d = Binary("0x00")
    ;***BMP header (54bytes total)***
    Local $Header = Binary("0x424D" & _                 ;2bytes, BM signature
            "00000000" & _                  ;4bytes, filesize (optional, omitted)
            "0000" & _                      ;2bytes, reserved
            "0000" & _                      ;2bytes, reserved
            "36000000" & _                  ;4bytes, offset to image data
            "28000000" & _                  ;4bytes, BITMAPINFOHEADER
            _Reverse8(Hex($Width, 8)) & _       ;4bytes, bitmap width
            _Reverse8(Hex($Height, 8)) & _  ;4bytes, bitmap hieght
            "0100" & _                      ;2bytes, bitmap planes
            "1800" & _                      ;2bytes, bitmap bitdepth
            "00000000" & _                  ;4bytes, bitmap compression type (none)
            _Reverse8(Hex(($Height) * _
            ($Width) * 3 + ($Height * $c), 8)) & _  ;4bytes, bitmap data size
            "00000000" & _                  ;4bytes, bitmap horizontal resolution (optional,omitted)
            "00000000" & _                  ;4bytes, bitmap vertical resolution (optional,omitted)
            "00000000" & _                  ;4bytes, bitmap colors (optional?, omitted)
            "00000000") ;4bytes, important colors (optional?, omitted)
    ;***End Header***
    Local $rowData = Binary("")
    Local $imageData = Binary("")
    For $n = 1 To $Width
        $rowData &= Binary("0xFFFFFF")
    Next
    $rowData &= $d
    For $m = 1 To $Height
        $imageData &= $rowData
    Next
    Local $BMPHandle[4]
    $BMPHandle[0] = $c
    $BMPHandle[1] = $Width
    $BMPHandle[2] = $Height
    $BMPHandle[3] = $Header & $imageData
    ConsoleWrite("_BMPCreate " & TimerDiff($begin) & @CRLF)
    Return $BMPHandle
EndFunc ;==>_BMPCreate

;Copied from BMP.au3 file
Func _Reverse8($inHex)
    Return StringMid($inHex, 7, 2) & StringMid($inHex, 5, 2) & StringMid($inHex, 3, 2) & StringMid($inHex, 1, 2)
EndFunc ;==>_Reverse8

;Copied from BMP.au3 file
Func _BMPWrite(ByRef $BMPHandle, $Fpath, $Progress = 1)
    If IsArray($BMPHandle) = False Then Return 0
    $out = FileOpen($Fpath, 18)
    If $out = -1 Then Return -1
    FileWrite($out, $BMPHandle[3])
    FileClose($out)
    Return 1
EndFunc ;==>_BMPWrite

;==================================================================
; http://www.autoitscript.com/forum/index.php?showtopic=25898&view=findpost&p=182505
; $s_file - file to load cursor from
; $i_cursor - system cursor to change
;==================================================================
Func _SetCursor($s_file, $i_cursor)
    Local $newhcurs, $lResult
    $newhcurs = DllCall("user32.dll", "int", "LoadCursorFromFile", "str", $s_file)
    If Not @error Then
        $lResult = DllCall("user32.dll", "int", "SetSystemCursor", "int", $newhcurs[0], "int", $i_cursor)
        If Not @error Then
            $lResult = DllCall("user32.dll", "int", "DestroyCursor", "int", $newhcurs[0])
        Else
            MsgBox(0, "Error", "Failed SetSystemCursor")
        EndIf
    Else
        MsgBox(0, "Error", "Failed LoadCursorFromFile")
    EndIf
EndFunc ;==>_SetCursor

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  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...