Moderators Melba23 Posted February 5, 2016 Author Moderators Share Posted February 5, 2016 gil900, However, the 2 methods produce significantly different answers when I test them: expandcollapse popup#include <GUIConstantsEx.au3> #include <ScreenCapture.au3> #include <Color.au3> _GDIPlus_Startup() $hex_avColor_1 = Screen_Area_Average_Color(10,10,400,400) ConsoleWrite($hex_avColor_1 & @CRLF) $hex_avColor_2 = _Area_Average_Colour(10, 10, 10, 400, 400) ConsoleWrite($hex_avColor_2 & @CRLF) $hGUI = GUICreate("Test", 500, 500) $cLabel_1 = GUICtrlCreateLabel("", 0, 0, 500, 250) GUICtrlSetBkColor($cLabel_1, "0x" & $hex_avColor_1) $cLabel_2 = GUICtrlCreateLabel("", 0, 250, 500, 250) GUICtrlSetBkColor($cLabel_2, "0x" & $hex_avColor_2) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Func Screen_Area_Average_Color($x_pos,$y_pos,$x_size,$y_size,$bRetunAsHex = 1) Local $Output = -1, _ $hHBmp = _ScreenCapture_Capture("", $x_pos, $y_pos, $x_size, $y_size) ;create a GDI bitmap by capturing full screen of the desktop If @error Then Return SetError(1) Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBmp) ;convert GDI bitmap to GDI+ bitmap If Not @error Then Local $hBitmap_Scaled = _GDIPlus_ImageResize($hBitmap, 1, 1) ;resize image to 1x1 If Not @error Then $Output = _GDIPlus_BitmapGetPixel($hBitmap_Scaled,0,0) ; Get color of the 1x1 pixel If Not @error Then If $bRetunAsHex Then $Output = Hex($Output,6) EndIf _GDIPlus_BitmapDispose($hBitmap_Scaled) ; release $hBitmap_Scaled EndIf _GDIPlus_BitmapDispose($hBitmap) ; release $hBitmap EndIf _WinAPI_DeleteObject($hHBmp) ;release GDI bitmap resource because not needed anymore If $Output = -1 Then Return SetError(1) Return $Output EndFunc ; #FUNCTION# ========================================================================================================= ; Name...........: _Area_Average_Colour ; Description ...: Returns average colour within a defined are of the display ; Syntax ........: _Area_Average_Colour([$iStep [, $iLeft [, $iTop [, $iWidth [, $iHeight ]]]]]) ; Parameters ....: $iStep - Pixel resolution value. Lower values give better resolution but take longer. (Default = 10) ; $iLeft - X coordinate of top-left corner of area (Default = 0 - left edge of screen) ; - If this parameter holds a window HANDLE, the area is set to the client area of the window ; $iTop - Y coordinate of top-left corner of area (Default = 0 - top edge of screen) ; $iWidth - Width of the area (Default = @DesktopWidth) ; $iHeight - Height of the area (Default = @DesktopHeight) ; Requirement(s) : v3.3.0.0 or higher ; Return values .: Success - Returns six character string containing RGB hex values ; Failure - Returns 0 and sets @error: ; 1 - Screen Capture failure ; 2 - GDI function failure ; Author ........: Melba23. Credit to Malkey for the basic GDI code ; Modified ......: ; Remarks .......: ; Example .......: Yes ;===================================================================================================================== Func _Area_Average_Colour($iStep = 10, $iLeft = 0, $iTop = 0, $iWidth = @DesktopWidth, $iHeight = @DesktopHeight) Local $iBlue = 0, $iGreen = 0, $iRed = 0, $iInterim_Blue = 0, $iInterim_Green = 0, $iInterim_Red = 0, $iInner_Count = 0, $iOuter_Count = 0 If IsHWnd($iLeft) Then $hWnd = $iLeft Local $tPoint = DllStructCreate("int X;int Y") DllStructSetData($tpoint, "X", 0) DllStructSetData($tpoint, "Y", 0) _WinAPI_ClientToScreen($hWnd, $tPoint) $iLeft = DllStructGetData($tPoint, "X") $iTop = DllStructGetData($tPoint, "Y") $aSize = WinGetClientSize($hWnd) $iWidth = $aSize[0] $iHeight = $aSize[1] EndIf _GDIPlus_Startup() Local $hBMP = _ScreenCapture_Capture("", $iLeft, $iTop, $iLeft + $iWidth, $iTop + $iHeight) If $hBMP = 0 Then _GDIPlus_Shutdown() Return SetError(1, 0, 0) EndIf Local $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBMP) If $hImage = 0 Then _WinAPI_DeleteObject($hBMP) ; <<<<<<<<<<<<< _GDIPlus_Shutdown() Return SetError(2, 0, 0) EndIf Local $tRes = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iWidth, $iHeight, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB) If @error Then _GDIPlus_BitmapDispose($hImage) ; <<<<<<<<<<<<< _WinAPI_DeleteObject($hBMP) ; <<<<<<<<<<<<< _GDIPlus_Shutdown() Return SetError(2, 0, 0) EndIf ;Get the returned values of _GDIPlus_BitmapLockBits() Local $iLock_Width = DllStructGetData($tRes, "width") Local $iLock_Height = DllStructGetData($tRes, "height") Local $iLock_Stride = DllStructGetData($tRes, "stride") Local $iLock_Scan0 = DllStructGetData($tRes, "Scan0") ; Run through the BitMap testing pixels at the step distance For $i = 0 To $iWidth - 1 Step $iStep For $j = 0 To $iHeight - 1 Step $iStep Local $v_Buffer = DllStructCreate("dword", $iLock_Scan0 + ($j * $iLock_Stride) + ($i * 4)) ; Get colour value of pixel Local $v_Value = DllStructGetData($v_Buffer, 1) ; Add components $iBlue += _ColorGetBlue($v_Value) $iGreen += _ColorGetGreen($v_Value) $iRed += _ColorGetRed($v_Value) ; Adjust counter $iInner_Count += 1 Next ; Determine average value so far - this prevents value becoming too large $iInterim_Blue += $iBlue / $iInner_Count $iBlue = 0 $iInterim_Green += $iGreen / $iInner_Count $iGreen = 0 $iInterim_Red += $iRed / $iInner_Count $iRed = 0 ; Adjust counters $iInner_Count = 0 $iOuter_Count += 1 Next ; Determine final average Local $avBlue = Hex(Int(Round($iInterim_Blue / $iOuter_Count, 0)), 2) Local $avGreen = Hex(Int(Round($iInterim_Green / $iOuter_Count, 0)), 2) Local $avRed = Hex(Int(Round($iInterim_Red / $iOuter_Count, 0)), 2) ; Clear up _GDIPlus_BitmapUnlockBits($hImage, $tRes) _GDIPlus_BitmapDispose($hImage) ; <<<<<<<<<<<<< _WinAPI_DeleteObject($hBMP) ; <<<<<<<<<<<<< _GDIPlus_Shutdown() Return ($avRed & $avGreen & $avBlue) EndFunc ;==>_Area_Average_Colour I have no idea which is the more correct - but it is something to note. M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Guest Posted February 5, 2016 Share Posted February 5, 2016 (edited) M23, I found bug in my code. I used _ScreenCapture_Capture worng in parameters Quote ... $iRight = -1 [, $iBottom = -1 I'm fixing it now. Edited February 5, 2016 by Guest Link to comment Share on other sites More sharing options...
Guest Posted February 5, 2016 Share Posted February 5, 2016 Try this: expandcollapse popup#include <GUIConstantsEx.au3> #include <ScreenCapture.au3> _GDIPlus_Startup() $hex_avColor = Area_Average_Color(10,10,400,400) ConsoleWrite($hex_avColor& @CRLF) Func Area_Average_Color($x_pos,$y_pos,$x_size,$y_size,$hWnd = 0,$bRetunAsHex = 1) Local $Output = -1,$hHBmp If Not $hWnd Then $hHBmp = _ScreenCapture_Capture("", $x_pos, $y_pos, $x_pos+$x_size, $y_pos+$y_size,False) ;create a GDI bitmap by capturing full screen of the desktop Else $hHBmp = _ScreenCapture_CaptureWnd('',$hWnd,$x_pos,$y_pos,$x_pos+$x_size,$y_pos+$y_size,False) EndIf If @error Then Return SetError(1,0,0) Local $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hHBmp) ;convert GDI bitmap to GDI+ bitmap If Not @error Then $Output = GetAverageColorFromGDIPlusImage($hBitmap,$bRetunAsHex) _GDIPlus_BitmapDispose($hBitmap) ; release $hBitmap EndIf If $Output = -1 Then Return SetError(1,0,0) Return $Output EndFunc Func GetAverageColorFromGDIPlusImage($hBitmap,$bRetunAsHex = 1) Local $Output = -1, _ $hBitmap_Scaled = _GDIPlus_ImageResize($hBitmap, 1, 1) ;resize image to 1x1 If Not @error Then $Output = _GDIPlus_BitmapGetPixel($hBitmap_Scaled,0,0) ; Get color of the 1x1 pixel If Not @error Then If $bRetunAsHex Then $Output = '0x'&Hex($Output,6) EndIf _GDIPlus_BitmapDispose($hBitmap_Scaled) ; release $hBitmap_Scaled EndIf If $Output = -1 Then Return SetError(1,0,0) Return $Output EndFunc Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted February 5, 2016 Author Moderators Share Posted February 5, 2016 gil900, I had already amended the test code I was using in a very similar manner and the results were still significantly different, no doubt because of the different methods of averaging the colour used by the 2 functions. As I said above, I have no idea (or even interest) in which one is more correct - all I wanted to point out was that the 2 functions produce different answers. M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
Guest Posted February 8, 2016 Share Posted February 8, 2016 (edited) On 5.2.2016 at 5:15 PM, Melba23 said: gil900, I had already amended the test code I was using in a very similar manner and the results were still significantly different, no doubt because of the different methods of averaging the colour used by the 2 functions. As I said above, I have no idea (or even interest) in which one is more correct - all I wanted to point out was that the 2 functions produce different answers. M23 I have not researched and checked it, but I thought about it logically. In order to resize image to be smaller - the algorithm must calculate the average color of [X groups of pixels]. (If the target size is 1x1 then X is always 1 - there is only one group of pixels which is all the pixels in the image) If the average color calculation is too much wrong, then you should see defect/s in the resized image. So the result of the resize process is an ultimate test for the average color calculation algorithm(that included in the resize function). And _GDIPlus_ImageResize() seems to produce very good looking result of smaller resized image. Because of this, I think it is right to conclude that the result is very correct and also tested(Tested but not intentionally) Edited February 8, 2016 by Guest Link to comment Share on other sites More sharing options...
pcguru000 Posted February 8, 2016 Share Posted February 8, 2016 Holy crap thank you guys for this really timely code example I'm working on a project to gather my average screen color every second or so and spit it back to a DMX controller for "surround lighting" via some LED strips. It's really close and here you guys just saved me a ton of time Link to comment Share on other sites More sharing options...
Moderators Melba23 Posted February 8, 2016 Author Moderators Share Posted February 8, 2016 pcguru000, That was exactly the reason for the original thread that led to my code - nice to see it being used again. M23 Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind Open spoiler to see my UDFs: Spoiler ArrayMultiColSort ---- Sort arrays on multiple columnsChooseFileFolder ---- Single and multiple selections from specified path treeview listingDate_Time_Convert -- Easily convert date/time formats, including the language usedExtMsgBox --------- A highly customisable replacement for MsgBoxGUIExtender -------- Extend and retract multiple sections within a GUIGUIFrame ---------- Subdivide GUIs into many adjustable framesGUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView itemsGUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeViewMarquee ----------- Scrolling tickertape GUIsNoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxesNotify ------------- Small notifications on the edge of the displayScrollbars ----------Automatically sized scrollbars with a single commandStringSize ---------- Automatically size controls to fit textToast -------------- Small GUIs which pop out of the notification area Link to comment Share on other sites More sharing options...
AndyG Posted February 8, 2016 Share Posted February 8, 2016 Hi, with a little help of AssembleIt64(), i wrote an ASM-function to calculate the average colour of the screen. The function returns the same result as Melba23´s script, (no wonder, i am add up all single color components and devide this result by the sum of all pixel ) It´s a little bit faster than Melba23´s script...a little....(500-600 times). 90% of the runtime of this function takes the BitBlt() because of M$-AERO, this could be dramatically improved by switch off AERO (improvement factor 10!!! ) so the whole function takes only a handful milliseconds. The script could be easily adapt to calculate parts of the screen or windows/controls, but I leave it to others to post scripts which can be easily copy&pasted expandcollapse popup;#include <assembleit2_64.au3> #include <WinAPI.au3> #include <Memory.au3> #AutoIt3Wrapper_UseX64=n #cs _averagepixel Use32 ;32Bit! mov edi,dword[esp+4] ;pointer bitmap mov ecx,dword[esp+8] ;width bitmap mov ebx,dword[esp+12] ;height bitmap mov eax,ebx ;h mul ecx ;w*h mov ecx,eax ;w*h=number of pixel movd xmm3,ecx ;0,0,0,w*h pshufd xmm3,xmm3,0 ;w*h,w*h,w*h,w*h sub ecx,1 ;from 0 to (w*h)-1 shl ecx,2 ;w*h*4 bytes mov edx,0 movd xmm1,edx ;xmm1 ARGB every pixel movd xmm2,edx ;xmm2 sumn all A,R,G,B ;_asmdbg_() @pixel_count: ;loop all pixel movd xmm0,[edi+ecx] ;pixel ARGB into xmm PUNPCKLBW xmm0,xmm1 ;000000000A0R0G0B PUNPCKLBW xmm0,xmm1 ;000A000R000G000B PADDD xmm2,xmm0 ;add all A,R,G,B Integer sub ecx,4 jnz @pixel_count ;loop all pixel movdqa xmm6,xmm2 ;save xmm2 A,R,G,B PSRLDQ xmm2,8 ;shift right 8 bytes = 0,0,A,R CVTDQ2PD xmm4,xmm2 ;convert int to double CVTDQ2PD xmm5,xmm3 ;convert int to double DIVPD xmm4,xmm5 ;divide sum_all_pixel / number_all_pixel CVTPD2DQ xmm7,xmm4 ;convert double to integer PSLLDQ xmm7,8 ;shift left 8 bytes = 000A000R00000000 ;_asmdbg_() MOVDQA xmm2,xmm6 ;restore xmm2 A,R,G,B CVTDQ2PD xmm4,xmm2 ;convert to double 0,0,G,B CVTDQ2PD xmm5,xmm3 ;convert to double DIVPD xmm4,xmm5 ;divide sum_all_pixel / number_all_pixel CVTPD2DQ xmm6,xmm4 ;convert double to integer = 00000000000G000B ORPS xmm7,xmm6 ;xmm7=000A000R000G000B =average colour all pixel PACKUSWB xmm7,xmm7 ; PACKUSWB xmm7,xmm7 ;ARGBARGBARGBARGB movd eax,xmm7 ;return average ARGB ret #ce $t = TimerInit() $average = Screen_Average_Colour() $m = Int(1000 * TimerDiff($t) / 1000) MsgBox(0, "Average Colour", "Average colour ARGB of screen = " & $average & @CRLF & "in " & $m & " milliseconds") Func Screen_Average_Colour() ;get desktop width and height local $hDLL_User32 = DllOpen("user32.dll") local $DesktopWidth = DllCall($hDLL_User32, "int", "GetSystemMetrics", "int", 78) ;sm_virtualwidth $DesktopWidth = $DesktopWidth[0] local $DesktopHeight = DllCall($hDLL_User32, "int", "GetSystemMetrics", "int", 79) ;sm_virtualheight $DesktopHeight = $DesktopHeight[0] Local $ptr_bitmap, $hbmp ;byref _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) $hdc_bitmap = _CreateNewBmp32($DesktopWidth, $DesktopHeight, $ptr_bitmap, $hbmp) ;create empty bitmap $hdc_desktop = _WinAPI_GetDC(0) ;hdc desktop (or window/control if you want) _WinAPI_BitBlt($hdc_bitmap, 0, 0, $DesktopWidth, $DesktopHeight, $hdc_desktop, 0, 0, 0xCC0020);bitblt desktop into bitmap ;$binarycode = _AssembleIt2("retbinary", "_averagepixel") ;gibt nur den assemblierten code zurück $binarycode = "0x8B7C24048B4C24088B5C240C89D8F7E189C1660F6ED9660F70DB0083E901C1E102BA00000000660F6ECA660F6ED2660F6E040F660F60C1660F60C1660FFED083E90475EA660F6FF2660F73DA08F30FE6E2F30FE6EB660F5EE5F20FE6FC660F73FF08660F6FD6F30FE6E2F30FE6EB660F5EE5F20FE6F40F56FE660F67FF660F67FF660F7EF8C3" ;nur für dllcalladdress() benötigt, den binarycode braucht man nur ein mal erstellen $tCodeBuffer = dllstructcreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes DllStructSetData($tCodeBuffer, 1, $binarycode) ;set asm-code into memory ;call asm-code $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $ptr_bitmap, "int_ptr", $DesktopWidth, "int_ptr", $DesktopHeight) _DeleteBitmap32($hdc_bitmap, $ptr_bitmap, $hbmp) ;Ressourcen freigeben Return Hex($ret[0], 8);get average colour EndFunc ;==>Screen_Average_Colour Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe $HDC und $ptr und handle auf die Bitmapdaten $hcdc = _WinAPI_CreateCompatibleDC(0) ;Desktop-Kompatiblen DeviceContext erstellen lassen $tBMI = DllStructCreate($tagBITMAPINFO) ;Struktur der Bitmapinfo erstellen und Daten eintragen DllStructSetData($tBMI, 1, DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette DllStructSetData($tBMI, 2, $iwidth) DllStructSetData($tBMI, 3, -$iheight) ;minus =standard = bottomup DllStructSetData($tBMI, 4, 1) DllStructSetData($tBMI, 5, 32) ;32 Bit = 4 Bytes => AABBGGRR $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0) $hbmp = $adib[0] ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden $ptr = $adib[4] ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet _WinAPI_SelectObject($hcdc, $hbmp) ;objekt hbitmap in DC Return $hcdc ;DC der Bitmap zurückgeben EndFunc ;==>_CreateNewBmp32 Func _DeleteBitmap32($DC, $ptr, $hbmp) _WinAPI_DeleteDC($DC) _WinAPI_DeleteObject($hbmp) $ptr = 0 EndFunc ;==>_DeleteBitmap32 LarsJ 1 Link to comment Share on other sites More sharing options...
UEZ Posted February 8, 2016 Share Posted February 8, 2016 (edited) I'm not to be able to hold a candle to AndyG but here we go. My slower, not optimized / non MMX/SSE, ASM version. expandcollapse popup#include <GDIPlus.au3> $sFile = FileOpenDialog("Select an image", "", "Images (*.jpg;*.bmp;*.png;*.gif;*.tif)") If @error Then Exit MsgBox(0, "Information", "Nothing selected") _GDIPlus_Startup() Global Const $hBitmap = _GDIPlus_BitmapCreateFromFile($sFile) Global $fTimer = TimerInit() Global $iResult = ASM_BitmapGetAverageColorValue($hBitmap) ConsoleWrite(TimerDiff($fTimer) & " ms" & @CRLF) MsgBox(0, "Test", "Average color value: 0x" & Hex($iResult, 8) & @CRLF) _GDIPlus_ImageDispose($hBitmap) _GDIPlus_Shutdown() Func ASM_BitmapGetAverageColorValue($hBitmap) ;coded by UEZ build 2016-02-08 Local $aDim = _GDIPlus_ImageGetDimension($hBitmap) If @error Then Return SetError(0, 0, 0) Local Const $iPixels = $aDim[0] * $aDim[1] Local $tBitmapData = _GDIPlus_BitmapLockBits($hBitmap, 0, 0, $aDim[0], $aDim[1], $GDIP_ILMREAD, $GDIP_PXF32ARGB) Local $pScan = $tBitmapData.Scan0 Local $iStride = $tBitmapData.Stride * 4 Local $tPixelData = DllStructCreate("dword[" & Abs($iStride * $aDim[1]) & "]", $pScan) Local $tStruct_ARGB = DllStructCreate("uint Alpha;uint Red;uint Green;uint Blue") Local $tCodeBuffer = DllStructCreate("byte ASM[75]") $tCodeBuffer.ASM = "0x8B7424048B7C24088B4C240C8B0689C381E3FF000000015F0C89C381E300FF0000C1EB08015F0889C381E30000FF00C1EB10015F0489C381E3000000FFC1EB18011F83C60483E90177C2C3" Local $aRet = DllCall("user32.dll", "none", "CallWindowProcW", "ptr", DllStructGetPtr($tCodeBuffer), _ "ptr", DllStructGetPtr($tPixelData), _ "ptr", DllStructGetPtr($tStruct_ARGB), _ "uint", $iPixels, _ "int", Null) _GDIPlus_BitmapUnlockBits($hBitmap, $tBitmapData) Local $iResult = 0x1000000 * Int($tStruct_ARGB.Alpha / $iPixels) + 0x10000 * Int($tStruct_ARGB.Red / $iPixels) + 0x100 * Int($tStruct_ARGB.Green / $iPixels) + Int($tStruct_ARGB.Blue / $iPixels) Return $iResult EndFunc For a 3000x30002 image it takes ~200 ms. Edited February 8, 2016 by UEZ nend 1 Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ Link to comment Share on other sites More sharing options...
AndyG Posted February 9, 2016 Share Posted February 9, 2016 @UEZ, you got my intention (and show it with your script ) how to use ASM to speed up AutoIt-Scripts dramatically. After searching/finding the "inner loop", write a really short, simple, and easy to understand ASM-code with the usage of only a handful ASM-opcodes... The rest should be done with Autoit. Very nice work, indeed! Btw. it´s absolutely unimportant and it does not matter how long the execution of the ASM-code takes! I don´t use BitmapLockBits() because of the gruesome (or grisely? what would a native english speaker choose?) runtime of this function... If the ASM-code is 500 to 1000 times faster than a loop in AutoIt, there is no need to discuss about some milliseconds. Therefore...thank you for reminding me my "intention" using ASM Link to comment Share on other sites More sharing options...
LarsJ Posted February 9, 2016 Share Posted February 9, 2016 UEZ, Please show the ASM-code. What do you think we can use such a bunch of numbers for? Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
AndyG Posted February 9, 2016 Share Posted February 9, 2016 (edited) @LarsJ, you could copy the "code" and insert it here... It´s an online disassembler, the right thing for such a piece of code... //EDIT He adds every A,R,G,B Colour component into the via DllStructCreate("uint Alpha;uint Red;uint Green;uint Blue") created memory. So after the ASM-code he has to divide those sum by the number of pixel. In my code, I did this too, but because of the need of precision, i store the sum of each component into a 64-bit-quadword. Then, because of the precision of division, these integer-quadwords have to transform into double-floatingpoint, then divide, and after that, transform the double into an 8-bit-integer.... UEZ´s version is so much easier....he did the "calculation" with AutoIt. But as mentioned above, nobody needs the complete function running within 3 milliseconds...i prefer his version I Edited February 9, 2016 by AndyG Link to comment Share on other sites More sharing options...
LarsJ Posted February 9, 2016 Share Posted February 9, 2016 I prefer complete code from UEZ with lots of comments, optimization hints, hints about porting to 64 bit, and that kind of things as he usually adds. Controls, File Explorer, ROT objects, UI Automation, Windows Message MonitorCompiled code: Accessing AutoIt variables, DotNet.au3 UDF, Using C# and VB codeShell menus: The Context menu, The Favorites menu. Shell related: Control Panel, System Image ListsGraphics related: Rubik's Cube, OpenGL without external libraries, Navigating in an image, Non-rectangular selectionsListView controls: Colors and fonts, Multi-line header, Multi-line items, Checkboxes and icons, Incremental searchListView controls: Virtual ListViews, Editing cells, Data display functions Link to comment Share on other sites More sharing options...
AndyG Posted February 9, 2016 Share Posted February 9, 2016 (edited) Here my version using Assembleit. If you insert more _asmdbg_() -lines into ASM-code, the debugger shows you how the code works. If you have the "Debugger Window" on the left side of the screen and the scite-Window on the right side, every time you will hit onto the "Next"-Button, in Scite window the line with the actual _asmdbg() will be marked...so you can step through your code AssembleIt64.zip expandcollapse popup#include <assembleit2_64.au3> #include <WinAPI.au3> #AutoIt3Wrapper_UseX64=n #cs _averagepixel ;Pixel ((A+B+C+D)/4) Use32 ;32Bit! mov edi,dword[esp+4] ;pointer bitmap mov ecx,dword[esp+8] ;width bitmap mov ebx,dword[esp+12] ;height bitmap mov eax,ebx ;h mul ecx ;w*h mov ecx,eax ;w*h=number of pixel movd xmm3,ecx ;0,0,0,w*h pshufd xmm3,xmm3,0 ;w*h,w*h,w*h,w*h sub ecx,1 ;from 0 to (w*h)-1 shl ecx,2 ;w*h*4 bytes mov edx,0 movd xmm1,edx ;xmm1 ARGB every pixel movd xmm2,edx ;xmm2 sumn all A,R,G,B _asmdbg_() @pixel_count: ;all pixel movd xmm0,[edi+ecx] ;pixel ARGB into xmm PUNPCKLBW xmm0,xmm1 ;000000000A0R0G0B PUNPCKLBW xmm0,xmm1 ;000A000R000G000B PADDD xmm2,xmm0 ;add all A,R,G,B sub ecx,4 jnz @pixel_count ;loop all pixel _asmdbg_() movdqa xmm6,xmm2 ;save xmm2 PSRLDQ xmm2,8 ;shift right 8 bytes = 0,0,A,R CVTDQ2PD xmm4,xmm2 ;convert to double CVTDQ2PD xmm5,xmm3 ;convert to double DIVPD xmm4,xmm5 ;divide sum_all_pixel / number_all_pixel CVTPD2DQ xmm7,xmm4 ;convert double to integer PSLLDQ xmm7,8 ;shift left 8 bytes = 000A000R00000000 _asmdbg_() MOVDQA xmm2,xmm6 ;restore xmm2 A,R,G,B CVTDQ2PD xmm4,xmm2 ;convert to double 0,0,G,B CVTDQ2PD xmm5,xmm3 ;convert to double DIVPD xmm4,xmm5 ;divide sum_all_pixel / number_all_pixel CVTPD2DQ xmm6,xmm4 ;convert double to integer = 00000000000G000B ORPS xmm7,xmm6 ;xmm7=000A000R000G000B =average color all pixel PACKUSWB xmm7,xmm7 ; PACKUSWB xmm7,xmm7 ;ARGBARGBARGBARGB movd eax,xmm7 ;return average ARGB ret #ce Global $hDLL_User32 = DllOpen("user32.dll") $DesktopWidth = DllCall($hDLL_User32, "int", "GetSystemMetrics", "int", 78) ;sm_virtualwidth $DesktopWidth = $DesktopWidth[0] $DesktopHeight = DllCall($hDLL_User32, "int", "GetSystemMetrics", "int", 79) ;sm_virtualheight $DesktopHeight = $DesktopHeight[0] Global $ptr_bitmap, $hbmp ;byref _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) $hdc_bitmap = _CreateNewBmp32($DesktopWidth, $DesktopHeight, $ptr_bitmap, $hbmp) ;hdc empty bitmap $hdc_desktop = _WinAPI_GetDC(0) ;hdc desktop _WinAPI_BitBlt($hdc_bitmap, 0, 0, $DesktopWidth, $DesktopHeight, $hdc_desktop, 0, 0, $SRCCOPY);bitblt desktop into bitmap ;############################################################################################# ;if you need the assembled code, uncomment the following lines and use DllCallAddress() ;~ $binarycode = _AssembleIt2("retbinary", "_averagepixel") ;gibt nur den assemblierten code zurück ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & StringLen($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console ;nur für dllcalladdress() benötigt, den binarycode braucht man nur ein mal erstellen ;~ $tCodeBuffer = _dllstructcreate64("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes ;~ DllStructSetData($tCodeBuffer, 1, $binarycode) ;~ $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $ptr_bitmap, "int_ptr", $DesktopWidth, "int_ptr", $DesktopHeight) ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console ;~ $ret = $ret[0] ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console ;############################################################################################# $ret = _AssembleIt2("uint", "_averagepixel", "ptr", $ptr_bitmap, "int_ptr", $DesktopWidth, "int_ptr", $DesktopHeight) $ret=hex($ret,8) MsgBox(262144, 'Debug line ~' & @ScriptLineNumber, 'Selection:' & @CRLF & '$ret' & @CRLF & @CRLF & 'Return:' & @CRLF & $ret) ;### Debug MSGBOX _DeleteBitmap32($hdc_bitmap, $ptr_bitmap, $hbmp) ;Ressourcen freigeben Func _CreateNewBmp32($iwidth, $iheight, ByRef $ptr, ByRef $hbmp) ;erstellt leere 32-bit-Bitmap; Rückgabe $HDC und $ptr und handle auf die Bitmapdaten $hcdc = _WinAPI_CreateCompatibleDC(0) ;Desktop-Kompatiblen DeviceContext erstellen lassen $tBMI = DllStructCreate($tagBITMAPINFO) ;Struktur der Bitmapinfo erstellen und Daten eintragen DllStructSetData($tBMI, 1, DllStructGetSize($tBMI) - 4);Structgröße abzüglich der Daten für die Palette DllStructSetData($tBMI, 2, $iwidth) DllStructSetData($tBMI, 3, -$iheight) ;minus =standard = bottomup DllStructSetData($tBMI, 4, 1) DllStructSetData($tBMI, 5, 32) ;32 Bit = 4 Bytes => AABBGGRR $adib = DllCall('gdi32.dll', 'ptr', 'CreateDIBSection', 'hwnd', 0, 'ptr', DllStructGetPtr($tBMI), 'uint', 0, 'ptr*', 0, 'ptr', 0, 'uint', 0) $hbmp = $adib[0] ;hbitmap handle auf die Bitmap, auch per GDI+ zu verwenden $ptr = $adib[4] ;pointer auf den Anfang der Bitmapdaten, vom Assembler verwendet _WinAPI_SelectObject($hcdc, $hbmp) ;objekt hbitmap in DC Return $hcdc ;DC der Bitmap zurückgeben EndFunc ;==>_CreateNewBmp32 Func _DeleteBitmap32($DC, $ptr, $hbmp) _WinAPI_DeleteDC($DC) _WinAPI_DeleteObject($hbmp) $ptr = 0 EndFunc ;==>_DeleteBitmap Edited February 9, 2016 by AndyG Link to comment Share on other sites More sharing options...
UEZ Posted February 9, 2016 Share Posted February 9, 2016 (edited) @LarsJ I wanted to see who will be interested in the code and ask for it and as Andy said I used a very simple ASM version for the summation: Func _ASM_BitmapCountARGBColors() _("use32") ;32Bit! _("mov esi, dword[esp+4]") ;points to beginning of the bitmap in the memory _("mov edi, dword[esp+8]") ;points to the beginning of the struct in the memory _("mov ecx, dword[esp+12]") ;amount of pixels to read (width * height) _("_loop1:") _("mov dword eax, [esi]") ;copy the current AARRGGBB value to eax _("mov ebx, eax") ;make a copy of eax for "faster" access _("and ebx, 0x000000FF") ;mask the blue channel _("add dword[edi + 12], ebx") ;add blue value which is saved in the struct -> each struct value is 4 bytes long and blue starts at memory + 12 _("mov dword ebx, eax") _("and ebx, 0x0000FF00") ;now the same with green channel _("shr ebx, 8") ;shift the green value to the right -> 0000GG00 - > 000000GG _("add dword[edi + 8], ebx") ;add green value _("mov dword ebx, eax") _("and ebx, 0x00FF0000") ;and red channel _("shr ebx, 16") ;-> 00RR0000 - > 000000RR _("add dword[edi + 4], ebx") ;add red value _("mov dword ebx, eax") _("and ebx, 0xFF000000") ;and finally with alpha channel _("shr ebx, 24") ;-> AA000000 - > 000000AA _("add dword[edi], ebx") ;add alpha value _("add esi, 4") ;get next color word from bitmap _("sub ecx, 1") ;decrease pixel count _("ja _loop1") ;if pixel count = zero then exit loop _("ret") ;finished EndFunc The code is straightforward and doesn't need much comments. If you have any question then please ask. I will try tomorrow to implement it as x64. Well Andy, if I were able to code how you code in ASM I would do it regardless whether it makes sense for a particular case. My ASM code is the result being a ASM rookie. Edited February 9, 2016 by UEZ added more comments to the ASM rookie code LarsJ 1 Please don't send me any personal message and ask for support! I will not reply! Selection of finest graphical examples at Codepen.io The own fart smells best! ✌Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!¯\_(ツ)_/¯ ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ 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