DrGamut Posted September 8, 2006 Share Posted September 8, 2006 I've written a function to scan a pixel region into an array and dump it into a 24 bit Windows Bitmap image. I'm a novice scripter at best so please don't tear apart my style and methods or lack thereof. This function is very slow and I would recommend its use be limited to debugging or non-critical output. I welcome all input and suggested optimizations. expandcollapse popup#include <String.au3> ;Example: PixelDumpBitmap('Untitled - Notepad',0,0,200,200,"notepad.bmp") Func PixelDumpBitmap($hWnd, $left, $top, $right, $bottom, $file, $PCM = 0) $OldCM = Opt("ColorMode", 1) $OldPCM = Opt('PixelCoordMode', $PCM) If IsString($hWnd) Then $hWnd = WinGetHandle($hWnd) Local $width = $right - $left + 1,$height = $bottom - $top + 1,$arrayX = 0,$arrayY = 0,$bitmap[$width][$height] If Not WinActive($hWnd) Then WinWaitActive($hWnd) For $y = $top To $bottom Step 1 For $x = $left To $right Step 1 $bitmap[$arrayX][$arrayY] = Hex(PixelGetColor($x,$y),6) $arrayX += 1 Next $arrayY += 1 $arrayX = 0 Next $headerType = "424D" $headerFileSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height + 54,8)) $headerReserved = "00000000" $headerOffset = "36000000" $headerInfoSize = "28000000" $headerWidth = _BigToLittle(Hex($width,8)) $headerHeight = _BigToLittle(Hex($height,8)) $headerPlanes = "0100" $headerBits = "1800" $headerCompression = "00000000" $headerImageSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height,8)) $headerXPelsPerMeter = "00000000" $headerYPelsPerMeter = "00000000" $headerClrUsed = "00000000" $headerClrImportant = "00000000" $bmp = FileOpen($file, 2) FileWrite($bmp, _HexToString($headerType & $headerFileSize & $headerReserved & $headerOffset & $headerInfoSize & $headerWidth & $headerHeight & $headerPlanes & $headerBits & $headerCompression & $headerImageSize & $headerXPelsPerMeter & $headerYPelsPerMeter & $headerClrUsed & $headerClrImportant)) For $y = $height - 1 To 0 Step -1 For $x = 0 To $width - 1 Step 1 FileWrite($bmp, _HexToString($bitmap[$x][$y])) Next FileWrite($bmp, _HexToString(_StringRepeat("00",Mod($width, 4)))) Next FileClose($bmp) Opt('ColorMode', $OldCM) Opt('PixelCoordMode', $OldPCM) Return 1 EndFunc Func _BigToLittle($Motorola) Local $Intel = "" For $i = 1 To StringLen($Motorola) Step 2 $Intel &= StringMid($Motorola,StringLen($Motorola) - $i,2) Next Return $Intel EndFunc Link to comment Share on other sites More sharing options...
DrGamut Posted September 9, 2006 Author Share Posted September 9, 2006 Anyone know what could make this script faster? I believe the pixel scanning itself is fairly speedy, but writing the file byte by byte consumes a lot of time. Link to comment Share on other sites More sharing options...
ramadash Posted September 9, 2006 Share Posted September 9, 2006 im not sure but maby if you compute ur whole string befor writing it ( $out &= $currPixie ) something like that, but I really dont know if it would help speed. I also noticed that dealing with big arrays seems very slow, for an example, on my p3 it takes ~20 sec to declare $array[160][120][7] and set all values to 0, im still looking for a way to speed up things. Another thing that I noticed is that you have those 2 loops:For $y = $top To $bottom Step 1 For $x = $left To $right Step 1 $bitmap[$arrayX][$arrayY] = Hex(PixelGetColor($x,$y),6) $arrayX += 1 Next $arrayY += 1 $arrayX = 0 Next For $y = $height - 1 To 0 Step -1 For $x = 0 To $width - 1 Step 1 FileWrite($bmp, _HexToString($bitmap[$x][$y])) Next FileWrite($bmp, _HexToString(_StringRepeat("00",Mod($width, 4)))) Next I think you could dump your bitmap array and use something like that (only a fast example) For $y = $top To $bottom Step 1 For $x = $left To $right Step 1 $outString &= _HexToString( Hex(PixelGetColor($x,$y),6) ) Next $outString &= _HexToString(_StringRepeat("00",Mod($width, 4))) Next this would probably speed up the process a little, but you still need to implement some stuff so the string is the correct format. Hope this can help btw that look very nice Link to comment Share on other sites More sharing options...
DrGamut Posted September 9, 2006 Author Share Posted September 9, 2006 (edited) Thank you for the response.I tried your suggestions, and quite to my own surprise it actually slowed the script further. I can't imagine why, as it's eliminating a loop and an array. With your suggestions in mind I modified the script to this:expandcollapse popup#include <String.au3> ;Example: PixelDumpBitmap('Untitled - Notepad',0,0,200,200,"notepad.bmp") Func PixelDumpBitmap($hWnd, $left, $top, $right, $bottom, $file, $PCM = 0) $OldCM = Opt("ColorMode", 1) $OldPCM = Opt('PixelCoordMode', $PCM) If IsString($hWnd) Then $hWnd = WinGetHandle($hWnd) Local $width = $right - $left + 1,$height = $bottom - $top + 1,$bitmap If Not WinActive($hWnd) Then WinWaitActive($hWnd) $headerType = "424D" $headerFileSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height + 54,8)) $headerReserved = "00000000" $headerOffset = "36000000" $headerInfoSize = "28000000" $headerWidth = _BigToLittle(Hex($width,8)) $headerHeight = _BigToLittle(Hex($height,8)) $headerPlanes = "0100" $headerBits = "1800" $headerCompression = "00000000" $headerImageSize = _BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height,8)) $headerXPelsPerMeter = "00000000" $headerYPelsPerMeter = "00000000" $headerClrUsed = "00000000" $headerClrImportant = "00000000" For $y = $bottom To $top Step -1 For $x = $left To $right Step 1 $bitmap &= Hex(PixelGetColor($x,$y),6) Next $bitmap &= _StringRepeat("00",Mod($width, 4)) Next $bmp = FileOpen($file, 2) FileWrite($bmp, _HexToString($headerType & $headerFileSize & $headerReserved & $headerOffset & $headerInfoSize & $headerWidth & $headerHeight & $headerPlanes & $headerBits & $headerCompression & $headerImageSize & $headerXPelsPerMeter & $headerYPelsPerMeter & $headerClrUsed & $headerClrImportant & $bitmap)) FileClose($bmp) Opt('ColorMode', $OldCM) Opt('PixelCoordMode', $OldPCM) Return 1 EndFunc Func _BigToLittle($Motorola) Local $Intel = "" For $i = 1 To StringLen($Motorola) Step 2 $Intel &= StringMid($Motorola,StringLen($Motorola) - $i,2) Next Return $Intel EndFuncI ran both the old script and the new script with my example command a few times each. The old script completed in ~14.3 seconds, and the new script completed in ~15.5 seconds.I wonder why.EDIT: More findings, I suspected running _HexToString on a very long string such as in the above example was eating up CPU time, and I was correct. By limiting the use of _HexToString to the results of each pixel read instead of using it on the completed output string I was able to reduce the completion time of the modified script to 13.3 seconds, beating the original. The function now looks like this:expandcollapse popup#include <String.au3> ;Example: PixelDumpBitmap('Untitled - Notepad',0,0,200,200,"notepad.bmp") Func PixelDumpBitmap($hWnd, $left, $top, $right, $bottom, $file, $PCM = 0) $OldCM = Opt("ColorMode", 1) $OldPCM = Opt('PixelCoordMode', $PCM) If IsString($hWnd) Then $hWnd = WinGetHandle($hWnd) Local $width = $right - $left + 1,$height = $bottom - $top + 1,$bitmap If Not WinActive($hWnd) Then WinWaitActive($hWnd) $headerType = _HexToString("424D") $headerFileSize = _HexToString(_BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height + 54,8))) $headerReserved = _HexToString("00000000") $headerOffset = _HexToString("36000000") $headerInfoSize = _HexToString("28000000") $headerWidth = _HexToString(_BigToLittle(Hex($width,8))) $headerHeight = _HexToString(_BigToLittle(Hex($height,8))) $headerPlanes = _HexToString("0100") $headerBits = _HexToString("1800") $headerCompression = _HexToString("00000000") $headerImageSize = _HexToString(_BigToLittle(Hex($width * $height * 3 + Mod($width, 4) * $height,8))) $headerXPelsPerMeter = _HexToString("00000000") $headerYPelsPerMeter = _HexToString("00000000") $headerClrUsed = _HexToString("00000000") $headerClrImportant = _HexToString("00000000") For $y = $bottom To $top Step -1 For $x = $left To $right Step 1 $bitmap &= _HexToString(Hex(PixelGetColor($x,$y),6)) Next $bitmap &= _StringRepeat("00",Mod($width, 4)) Next $bmp = FileOpen($file, 2) FileWrite($bmp, $headerType & $headerFileSize & $headerReserved & $headerOffset & $headerInfoSize & $headerWidth & $headerHeight & $headerPlanes & $headerBits & $headerCompression & $headerImageSize & $headerXPelsPerMeter & $headerYPelsPerMeter & $headerClrUsed & $headerClrImportant & $bitmap) FileClose($bmp) Opt('ColorMode', $OldCM) Opt('PixelCoordMode', $OldPCM) Return 1 EndFunc Func _BigToLittle($Motorola) Local $Intel = "" For $i = 1 To StringLen($Motorola) Step 2 $Intel &= StringMid($Motorola,StringLen($Motorola) - $i,2) Next Return $Intel EndFunc Edited September 9, 2006 by DrGamut Link to comment Share on other sites More sharing options...
ramadash Posted September 9, 2006 Share Posted September 9, 2006 (edited) hmm weird indeed maby someone more experienced could tell us why it is slower (I would personally really like to know too) btw im gonna go do some research i will edit this post later, im sure there is a way to deal directly with file streams so all this convertion wont be needed (maby hex()) and for the variables at the top, do you really need to convert them to string? like could u, instead of: -$headerOffset = _HexToString("36000000") do this? -$headerOffset = 0x36000000 because autoit does have hex syntax edit: have you looked at this? CreateBitmap The CreateBitmap function creates a bitmap with the specified width, height, and color format (color planes and bits-per-pixel). HBITMAP CreateBitmap( int nWidth, // bitmap width, in pixels int nHeight, // bitmap height, in pixels UINT cPlanes, // number of color planes UINT cBitsPerPel, // number of bits to identify color CONST VOID *lpvBits // color data array ); Parameters nWidth [in] Specifies the bitmap width, in pixels. nHeight [in] Specifies the bitmap height, in pixels. cPlanes [in] Specifies the number of color planes used by the device. cBitsPerPel [in] Specifies the number of bits required to identify the color of a single pixel. lpvBits [in] Pointer to an array of color data used to set the colors in a rectangle of pixels. Each scan line in the rectangle must be word aligned (scan lines that are not word aligned must be padded with zeros). If this parameter is NULL, the contents of the new bitmap is undefined. Return Values If the function succeeds, the return value is a handle to a bitmap. If the function fails, the return value is NULL. while dealing with arrays might be slow, it will be alot faster than dealing with strings (strings are probably the slower variables to process on every languages) so it might worth a try. Edited September 9, 2006 by ramadash Link to comment Share on other sites More sharing options...
DrGamut Posted September 11, 2006 Author Share Posted September 11, 2006 hmm weird indeed maby someone more experienced could tell us why it is slower (I would personally really like to know too) btw im gonna go do some research i will edit this post later, im sure there is a way to deal directly with file streams so all this convertion wont be needed (maby hex()) and for the variables at the top, do you really need to convert them to string? like could u, instead of: -$headerOffset = _HexToString("36000000") do this? -$headerOffset = 0x36000000 because autoit does have hex syntax edit: have you looked at this? CreateBitmap The CreateBitmap function creates a bitmap with the specified width, height, and color format (color planes and bits-per-pixel). HBITMAP CreateBitmap( int nWidth, // bitmap width, in pixels int nHeight, // bitmap height, in pixels UINT cPlanes, // number of color planes UINT cBitsPerPel, // number of bits to identify color CONST VOID *lpvBits // color data array ); Parameters nWidth [in] Specifies the bitmap width, in pixels. nHeight [in] Specifies the bitmap height, in pixels. cPlanes [in] Specifies the number of color planes used by the device. cBitsPerPel [in] Specifies the number of bits required to identify the color of a single pixel. lpvBits [in] Pointer to an array of color data used to set the colors in a rectangle of pixels. Each scan line in the rectangle must be word aligned (scan lines that are not word aligned must be padded with zeros). If this parameter is NULL, the contents of the new bitmap is undefined. Return Values If the function succeeds, the return value is a handle to a bitmap. If the function fails, the return value is NULL. while dealing with arrays might be slow, it will be alot faster than dealing with strings (strings are probably the slower variables to process on every languages) so it might worth a try. What is that bitmap function from and how do I use it? I interpreted your first post as suggesting I use a string instead of an array, I suppose you only meant to use it to avoid accessing the output file multiple times. So I should keep my array creation loop then? Link to comment Share on other sites More sharing options...
cppman Posted September 11, 2006 Share Posted September 11, 2006 the reason it is slow is because of the loops you have to go through to get every pixel in the area. There is'nt really much you can do about that. You could try to write it in C++ and that will increase the speed dramatically! Miva OS Project Link to comment Share on other sites More sharing options...
DrGamut Posted September 11, 2006 Author Share Posted September 11, 2006 (edited) the reason it is slow is because of the loops you have to go through to get every pixel in the area. There is'nt really much you can do about that. You could try to write it in C++ and that will increase the speed dramatically!In a test I did with my original function script and a 40,000 pixel area on an AMD 2500+, it took 0.9 seconds to scan the pixels into an array and ~13.4 seconds to write them into a file. Edited September 11, 2006 by DrGamut Link to comment Share on other sites More sharing options...
Lazycat Posted September 11, 2006 Share Posted September 11, 2006 In a test I did with my original function script and a 40,000 pixel area on an AMD 2500+, it took 0.9 seconds to scan the pixels into an array and ~13.4 seconds to write them into a file.Take a look for BitBlt function - this did the things alot faster. Here the UDF you can start from. Koda homepage ([s]Outdated Koda homepage[/s]) (Bug Tracker)My Autoit script page ([s]Outdated mirror[/s]) Link to comment Share on other sites More sharing options...
ramadash Posted September 12, 2006 Share Posted September 12, 2006 What is that bitmap function from and how do I use it?I interpreted your first post as suggesting I use a string instead of an array, I suppose you only meant to use it to avoid accessing the output file multiple times. So I should keep my array creation loop then?this bitmap function is from (I believe) Kernel32.dll (not sure 100%?). The win32 API functions are documented on msdn.com (search win32 api documentation). Btw it would be better to use MakeCompatibleBitmap Instaed of MAkeBitamp (if you want to make color bmps atleast). This function can be used with dllcall (maby COM also but i dontt hink so).My first suggestion was this, but not to get rid of the array. Because you had 2 loops: 1 that put the values in an array, and another that parse that array and use filewrite, what I was saying is that all this could be done with 1 loop, so I dont think the array is needed since instead of holding the data, you can deal with it immediatly, but it should not make a very big difference anyway, but I realyl think that calling multiple filewriteline is slower than appending to a string and using filewrite.well all those are supposition we would need to do some concrete tests to see what is fastest but that create bitmap function seem very insteresting! Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now