Bilgus Posted April 29, 2020 Posted April 29, 2020 (edited) This is an update or derivative work of Beege 's Scrolling Line Graph UDF https://www.autoitscript.com/forum/topic/109599-scrolling-line-graph-udf I noticed a few issues for my use case with the UDF one being that adding a sample required updating the waveform High CPU usage went hand in hand with that requirement Another issue was just how long updating took to complete I've hopefully rectified that with this version There are a few changes (only 1 line per graph for instance) The addition of a function AddSample (uses graphics paths to speed up drawing samples on update) Gridlines are only generated once A sample finished line can be added UpdateGraph allows you to compress the discarded portion of the graph (it looks kinda cool but uses more CPU) Lower Cpu usage Uses real Control Ids - it is a label control underneath so you get click events and can display text when control is disabled Example (Waveform.au3) Reveal hidden contents waveform.au3 expandcollapse popup#include <GUIConstantsEx.au3> ; $GUI_DISABLE #include "SSLG.au3" #include <Array.au3> ;Just some timing variables Global $iAvg = 0, $iAvg_Add = 0 Global $iCt = 0, $iCt_Add = 0 Global $iMax = 0, $iMin = 60000 ;Setup Global Const $Increments = 100 Global $UpdateIntervalMs = 100 Global $iSleep = 25 Global $bGenSamples = True Opt('GuiOnEventMode', 1) $GUI = GUICreate("GDI+ Scrolling Single Line Graph", 800, 500, -1, -1, BitOR($WS_SIZEBOX, $WS_SYSMENU, $WS_MINIMIZEBOX)) OnAutoItExitRegister(_Exit) GUISetOnEvent(-3, _Exit) GUISetState() ;_SSLG_CreateGraph($iLeft, $iTop, $iWidth, $iHeight, $iY_Min, $iY_Max, $iIncrements, $iBackGround = Default) Global $Graph1 = _SSLG_CreateGraph(10, 10, -20, 300, 0, 100, $Increments) GUICtrlSetData(-1, "Click me to enable" & @CRLF & "Resize the window to your liking" & @CRLF & "Click Again for a randomized experience") GUICtrlSetOnEvent(-1, graph_) GUICtrlSetState(-1, BitOR(GUICtrlGetState(-1), $GUI_DISABLE)) _SSLG_ClearGraph($Graph1) ;Create already cleared the graph ;Only needed for the example ================================================== Global $Slider1 = GUICtrlCreateSlider(35, 350, 800 - 45, 40) GUICtrlSetLimit(-1, 100, 1) GUICtrlSetData(-1, $iSleep) GUICtrlSetOnEvent(-1, slider_) GUICtrlSetTip(-1, $iSleep) UpdateSpeed($iSleep) Global $Checkbox1 = GUICtrlCreateCheckbox("", 10, 350, 25, 25) GUICtrlSetState(-1, $GUI_CHECKED) GUICtrlSetOnEvent(-1, checkbox_) ;============================================================================== GUIRegisterMsg($WM_EXITSIZEMOVE, WM_EXITSIZEMOVE) ;Wait for first activation While BitAND(GUICtrlGetState($Graph1), $GUI_DISABLE) = $GUI_DISABLE Sleep(1000) WEnd AdlibRegister(Update, $UpdateIntervalMs) Local $i = 0, $t Local $y While 1 $t = TimerInit() If $bGenSamples Then ;_SSLG_AddSample($Graph1, $i) $y = Int(Sin($i / 30) * 40) + 45 _SSLG_AddSample($Graph1, $y) _SSLG_AddSample($Graph1, Random($y / 2, $y * 2)) For $j = 0 To Random(1, 10, 1) _SSLG_AddSample($Graph1, Random(0, 100)) Next Else For $j = 0 To 5 _SSLG_AddSample($Graph1) Next EndIf $iAvg_Add += TimerDiff($t) $iCt_Add += 1 Sleep($iSleep) $i += Random(1, 15, 1) If $i > 179 Then $i = 0 WEnd Func Update() Local $iErr, $iSamples Local $bCompress, $bFill Local $t = TimerInit() Static $i = 0 $i = $i + 1 If $i > 0 Then $bCompress = True $bFill = False Else $bCompress = False $bFill = True EndIf ;_SSLG_UpdateGraph($idSSLG, $bCompress = False, $bFill = False) If _SSLG_UpdateGraph($Graph1, $bCompress, $bFill) Then $iSamples = @extended Local $iT = TimerDiff($t) If $iT > $iMax Then $iMax = $iT If $iT < $iMin Then $iMin = $iT $iAvg += $iT $iCt += 1 If $iCt >= 1000 Then Exit EndIf If $i > 100 Then $i = -100 EndFunc ;==>Update Func UpdateSpeed($iSleepMs) ;Try to keep update interval in sync with sample generation $UpdateIntervalMs = $iSleepMs * 10 AdlibRegister(Update, $UpdateIntervalMs) EndFunc ;==>UpdateSpeed ;Callbacks ==================================================================== Func WM_EXITSIZEMOVE($hWnd, $Msg, $wParam, $lParam) _SSLG_SetResizeGraph($Graph1) Return $GUI_RUNDEFMSG EndFunc ;==>WM_EXITSIZEMOVE Func slider_() $iSleep = GUICtrlRead($Slider1) GUICtrlSetTip($Slider1, $iSleep) UpdateSpeed($iSleep) EndFunc ;==>slider_ Func checkbox_() $bGenSamples = (GUICtrlRead($Checkbox1) = $GUI_CHECKED) EndFunc ;==>checkbox_ Func graph_() ConsoleWrite("*Click*" & @CRLF) Local $iState = GUICtrlGetState($Graph1) If Not BitAND($iState, $GUI_DISABLE) Then Sleep(1000) GUICtrlSetData($Graph1, "Click me to enable") GUICtrlSetState($Graph1, BitOR($iState, $GUI_DISABLE)) Return Else Local $iY_Min = Random(0, 1, 1) Local $iY_Max = ($iY_Min - 1) * -100 $iY_Min *= 100 _SSLG_SetYRange($Graph1, $iY_Min, $iY_Max) GUICtrlSetData($Graph1, "") EndIf Local $iGrid = Random(-5, 50, 1) If $iGrid < 0 Then $iGrid = 0 GUICtrlSetState($Graph1, BitXOR($iState, $GUI_DISABLE)) ;_SSLG_SetSetSampleIndicator($idSSLG, $iLineColor = False, $iPenSize = Default) If Random(0, 1, 1) = 0 Then _SSLG_SetSetSampleIndicator($Graph1, 0xffFF0000, 2) Else _SSLG_SetSetSampleIndicator($Graph1, False) EndIf ;_SSLG_SetGrid($idSSLG, $iX_c, $iY_c, $iGridColor = Default, $iPenSize = Default) _SSLG_SetGrid($Graph1, $iGrid, $iGrid / 2) Local $iColor = Random(0x0, 0xFF, 1) + Random(0x0, 0xFF, 1) * 256 + Random(0x0, 0xFF, 1) * 65536 ;_SSLG_SetLine($idSSLG, $iLineColor, $iPenSize = Default, $iFillColor = Default) _SSLG_SetLine($Graph1, 0xFF00FF00, Random(1, 5, 1), $iColor) GUICtrlSetBkColor($Graph1, $iColor) EndFunc ;==>graph_ Func _Exit() AdlibUnRegister(Update) Local $sRes = StringFormat("Update: %dx Avg %0.2fMs\r\nMin %0.2fMs, Max %0.2fMs\r\n\r\nAdd: %dx Avg %0.2fMs\r\n", $iCt, ($iAvg / $iCt), $iMin, $iMax, $iCt_Add, ($iAvg_Add / $iCt_Add)) ConsoleWrite($sRes) MsgBox(0, @ScriptName, $sRes) OnAutoItExitUnRegister(_Exit) Exit EndFunc ;==>_Exit Example 2 (peak.au3) Reveal hidden contents expandcollapse popup#include <GuiConstants.au3> #include "SSLG.au3" Global $oAudioMeterInformation = _AudioVolObject() If Not IsObj($oAudioMeterInformation) Then Exit -1 ; Will happen on non-supported systems Opt('GuiOnEventMode', 1) Global $GUI = GUICreate("GDI+ Scrolling Single Line Graph", 800, 500, -1, -1, BitOR($WS_SIZEBOX, $WS_SYSMENU, $WS_MINIMIZEBOX)) OnAutoItExitRegister(_Exit) GUISetOnEvent(-3, _Exit) ;_SSLG_CreateGraph($iLeft, $iTop, $iWidth, $iHeight, $iY_Min, $iY_Max, $iIncrements, $iBackGround = Default) Global $Graph1 = _SSLG_CreateGraph(10, 10, -20, 300, 0, 100, 600) GUICtrlSetOnEvent(-1, graph_) ;_SSLG_SetGrid($idSSLG, $iX_c, $iY_c, $iGridColor = Default, $iPenSize = Default) _SSLG_SetGrid($Graph1, 100, 10) _SSLG_ClearGraph($Graph1) ;Create already cleared the graph but we want a grid GUISetState() AdlibRegister(_SamplePeak, 25) AdlibRegister(_UpdateGraph, 300) GUIRegisterMsg($WM_EXITSIZEMOVE, WM_EXITSIZEMOVE) While 1 Sleep(1000) WEnd Func _SamplePeak() Local Const $S_OK = 0 Local $iPeak If $oAudioMeterInformation.GetPeakValue($iPeak) = $S_OK Then _SSLG_AddSample($Graph1, 100 * $iPeak) EndIf EndFunc ;==>_SamplePeak Func _UpdateGraph() ;_SSLG_UpdateGraph($idSSLG, $bCompress = False, $bFill = False) _SSLG_UpdateGraph($Graph1, True, False) EndFunc ;==>_UpdateGraph Func WM_EXITSIZEMOVE($hWnd, $Msg, $wParam, $lParam) _SSLG_SetResizeGraph($Graph1) Return $GUI_RUNDEFMSG EndFunc ;==>WM_EXITSIZEMOVE Func graph_() ConsoleWrite("*Click*" & @CRLF) EndFunc ;==>graph_ Func _Exit() AdlibUnRegister(_SamplePeak) AdlibUnRegister(_UpdateGraph) OnAutoItExitUnRegister(_Exit) Exit EndFunc ;==>_Exit ;------------------------------------------------------------------------------------------------------------------------------------- ; https://www.autoitscript.com/forum/topic/142523-master-loudnessvolume-via-peak-meter-windows-7-64-bit/?tab=comments#comment-1003940 ;------------------------------------------------------------------------------------------------------------------------------------- Func _AudioVolObject() Local Const $sCLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}" Local Const $sIID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}" Local Const $tagIMMDeviceEnumerator = "EnumAudioEndpoints hresult(dword;dword;ptr*);" & _ "GetDefaultAudioEndpoint hresult(dword;dword;ptr*);" & _ "GetDevice hresult(wstr;ptr*);" & _ "RegisterEndpointNotificationCallback hresult(ptr);" & _ "UnregisterEndpointNotificationCallback hresult(ptr);" ; Sequences of code below are taken from the source of plugin written for AutoIt for setting master volume on Vista and above systems. ; Code was written by wraithdu in C++. ; MMDeviceEnumerator Local $oMMDeviceEnumerator = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $tagIMMDeviceEnumerator) If @error Then Return SetError(1, 0, 0) Local Const $eRender = 0 Local Const $eConsole = 0 ; DefaultAudioEndpoint Local $pDefaultDevice $oMMDeviceEnumerator.GetDefaultAudioEndpoint($eRender, $eConsole, $pDefaultDevice) If Not $pDefaultDevice Then Return SetError(2, 0, 0) ; Turn that pointer into object Local $oIMMDefaultDevice = ObjCreateInterface($pDefaultDevice, "{D666063F-1587-4E43-81F1-B948E807363F}", _ "Activate hresult(clsid;dword;variant*;ptr*);") Local Const $CLSCTX_INPROC_SERVER = 0x1 ; AudioMeterInformation Local $pAudioMeterInformation $oIMMDefaultDevice.Activate("{C02216F6-8C67-4B5B-9D00-D008E73E0064}", $CLSCTX_INPROC_SERVER, 0, $pAudioMeterInformation) If Not $pAudioMeterInformation Then Return SetError(3, 0, 0) Return ObjCreateInterface($pAudioMeterInformation, "{C02216F6-8C67-4B5B-9D00-D008E73E0064}", "GetPeakValue hresult(float*);") EndFunc ;==>_AudioVolObject ;------------------------------------------------------------------------------------------------------------------------------------- ; https://www.autoitscript.com/forum/topic/142523-master-loudnessvolume-via-peak-meter-windows-7-64-bit/?tab=comments#comment-1003940 ;------------------------------------------------------------------------------------------------------------------------------------- UDF Reveal hidden contents SSLG.au3 expandcollapse popup; #Region Header ; #INDEX# ======================================================================================================================= ; Title ............: SSLG ; AutoIt Version ...: 3.3.14.5 ; Language .........: English ; Description ......: Functions to assist in creating and updating Scrolling Single Line Graphs ; Original Author(s): Beege ;http://www.autoitscript.com/forum/index.php?showtopic=109599 ; Author(s) ........: Bilgus ; =============================================================================================================================== ; #CURRENT# ===================================================================================================================== ;_SSLG_CreateGraph ;_SSLG_AddSample ;_SSLG_UpdateGraph ;_SSLG_SetSetSampleIndicator ;_SSLG_SetGrid ;_SSLG_SetLine ;_SSLG_SetSmoothingMode ;_SSLG_ClearGraph ;_SSLG_SetYRange ;_SSLG_SetBackGroundColor ; =============================================================================================================================== #include-once #include <GDIPlus.au3> #include <WindowsConstants.au3> _GDIPlus_Startup() OnAutoItExitRegister(__SSLG_Exit) #EndRegion Header #Region Global Variables and Constants Global Const $gSSLG_DICT = 1 Global Enum $gSSLG_idCtrl, $gSSLG_aRect, $gSSLG_hCtrl, $gSSLG_hPen, $gSSLG_hPenSamp, $gSSLG_hPenGrid, $gSSLG_hBrushLine, $gSSLG_hBrushBkgnd, _ $gSSLG_hDCBuf, $gSSLG_hHBitmapGDI_, $gSSLG_hBitmap_1, $gSSLG_hGrBuf_1, $gSSLG_hBitmap_0, $gSSLG_hGrBuf_0, $gSSLG_hPathGridH, _ $gSSLG_iY_Min, $gSSLG_iY_Max, $gSSLG_iY_Range, $gSSLG_iY_Last, $gSSLG_iX_Inc, $gSSLG_hPathLine, $gSSLG_hPathGridV, $gSSLG_hMatrixHT, _ $gSSLG_bNeedResize, $gSSLG_iIncrements, $gSSLG_iIncrementSize, $gSSLG_iBackColor, $gSSLG_iSamples, $gSSLG_iSamplesTotal, $MAX Global $g_SSLG[1][$MAX] $g_SSLG[0][0] = 0 $g_SSLG[0][$gSSLG_DICT] = ObjCreate("Scripting.Dictionary") #cs $g_SSLG[0][0] = List Count [0][1] = Dictionary object (associative array) [0][2-27] = Nothing [$i][$gSSLG_idCtrl] = ControlID [$i][$gSSLG_aRect] = Control position array [x, y, w, h] [$i][$gSSLG_hCtrl] = Control Handle [$i][$gSSLG_hPen] = Line Pen Handle [$i][$gSSLG_hPenSamp] = Sample Finished Pen Handle [$i][$gSSLG_hPenGrid] = Gridline Pen Handle [$i][$gSSLG_hBrushLine] = Brush to fill under graph line [$i][$gSSLG_hBrushBkgnd] = Brush for background ([$gSSLG_iBackColor]) [$i][$gSSLG_hDCBuf] = Device context Handle for buffer [$i][$gSSLG_hHBitmapGDI_] = Standard GDI bitmap (selected into hDCBuf) [$i][$gSSLG_hBitmap_1] = Buffer 1 Bitmap Object Handle [$i][$gSSLG_hGrBuf_1] = Buffer 1 Graphic Object Handle [$i][$gSSLG_hBitmap_0] = Buffer 0 Bitmap Object Handle [$i][$gSSLG_hGrBuf_0] = Buffer 0 Graphic Object Handle [$i][$gSSLG_iY_Min] = Y Min [$i][$gSSLG_iY_Max] = Y Max [$i][$gSSLG_iY_Range] = Y Range [$i][$gSSLG_iY_Last] = Last Y Coordinate [$i][$gSSLG_iX_Inc] = Vertical Gridline every #iX_Inc samples [$i][$gSSLG_hPathLine] = Graphics Path Object Handle [$i][$gSSLG_hPathGridV] = Graphics Path Object Handle Vertical Grid Lines [$i][$gSSLG_hPathGridH] = Graphics Path Object Handle Horizontal Grid Lines [$i][$gSSLG_hMatrixHT] = Graphics Path Horizontal Translation Matrice [$i][$gSSLG_bNeedResize] = Resize graphics objects [$i][$gSSLG_iIncrements] = Step Count [$i][$gSSLG_iIncrementSize] = Step Size [$i][$gSSLG_iBackColor] = Background Color value [$i][$gSSLG_iSamples] = Count of samples waiting to be drawn [$i][$gSSLG_iSamplesTotal] = Count of total samples since ClearGraph() #ce #EndRegion Global Variables and Constants #Region Public Functions ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_CreateGraph ; Description ...: Creates a Scrolling Line Graph ; Syntax.........: _SSLG_CreateGraph($iLeft, $iTop, $iW, $iH, $iY_Min, $iY_Max, $iIncrements, $iBackGround = Default) ; Parameters ....: ; $iLeft - Left side of the graph ; $iTop - Top side of the graph ; $iW - Width of the graph ; $iH - Height of the graph ; $iY_Min - Minimum Y Value ; $iY_Max - Maximum Y Value ; $iIncrements - How many parts the Graph is divided up into. ; $iBackGround - BackGround Color. Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB). Default = Black ; Return values .: Success - ControlId ; Author ........: Beege, Bilgus ; Remarks .......: $iIncrements will be rounded if $iW is not evenly divisible ; =============================================================================================================================== Func _SSLG_CreateGraph($iLeft, $iTop, $iW, $iH, $iY_Min, $iY_Max, $iIncrements, $iBackGround = Default) If $iBackGround = Default Then $iBackGround = 0xFF000000 ReDim $g_SSLG[UBound($g_SSLG) + 1][UBound($g_SSLG, 2)] $g_SSLG[0][0] += 1 Local $oDict = $g_SSLG[0][$gSSLG_DICT] Local $iIdx = $g_SSLG[0][0] Local $aResult, $aPos Local $iCtrlID = GUICtrlCreateLabel("", $iLeft, $iTop, $iW, $iH, $WS_CLIPSIBLINGS) Local $hCtrl = GUICtrlGetHandle($iCtrlID) If $iW < 0 Or $iH < 0 Then ;Allow -W/H $aResult = DllCall("user32.dll", "hwnd", "GetParent", "hwnd", $hCtrl) If Not @error Then $aPos = WinGetClientSize($aResult[0]) If Not @error Then If $iW < 0 Then $iW = $aPos[0] + $iW If $iH < 0 Then $iH = $aPos[1] + $iH GUICtrlSetPos($iCtrlID, $iLeft, $iTop, $iW, $iH) EndIf EndIf EndIf $g_SSLG[$iIdx][$gSSLG_idCtrl] = $iCtrlID $g_SSLG[$iIdx][$gSSLG_hCtrl] = $hCtrl Local $hDC = _WinAPI_GetDC($hCtrl) $g_SSLG[$iIdx][$gSSLG_hDCBuf] = _WinAPI_CreateCompatibleDC($hDC) _WinAPI_ReleaseDC($hCtrl, $hDC) ;Store references $oDict.Add(Hex($iCtrlID), $iIdx) $oDict.Add(Hex($hCtrl), $iIdx) $g_SSLG[$iIdx][$gSSLG_iIncrements] = $iIncrements $g_SSLG[$iIdx][$gSSLG_hPathLine] = _GDIPlus_PathCreate() ;Create new path object $g_SSLG[$iIdx][$gSSLG_hPathGridV] = _GDIPlus_PathCreate() ;Create new path object $g_SSLG[$iIdx][$gSSLG_hPathGridH] = _GDIPlus_PathCreate() ;Create new path object $g_SSLG[$iIdx][$gSSLG_hMatrixHT] = _GDIPlus_MatrixCreate() ;Horizontal translation matrix ;Initially these are disabled; use _SetGrid & _SetSampleIndicator $g_SSLG[$iIdx][$gSSLG_hPenSamp] = False $g_SSLG[$iIdx][$gSSLG_hPenGrid] = False $g_SSLG[$iIdx][$gSSLG_iBackColor] = __SSLG_RGB_ARGB($iBackGround) $g_SSLG[$iIdx][$gSSLG_hBrushBkgnd] = _GDIPlus_BrushCreateSolid($g_SSLG[$iIdx][$gSSLG_iBackColor]) GUICtrlSetBkColor(-1, BitAND(BitNOT(0xFF000000), $iBackGround)) GUICtrlSetColor(-1, 0xFFFFFF) _SSLG_SetLine($iCtrlID, 0xFF00FF00, 2) $g_SSLG[$iIdx][$gSSLG_iY_Last] = False $g_SSLG[$iIdx][$gSSLG_iX_Inc] = 0 $g_SSLG[$iIdx][$gSSLG_iSamples] = 0 $g_SSLG[$iIdx][$gSSLG_iSamplesTotal] = 0 _SSLG_SetYRange($iCtrlID, $iY_Min, $iY_Max) _SSLG_SetSmoothingMode($iCtrlID) $g_SSLG[$iIdx][$gSSLG_bNeedResize] = True _SSLG_ClearGraph($iCtrlID) Return $iCtrlID EndFunc ;==>_SSLG_CreateGraph ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_AddSample ; Description ...: Adds a new value to Line Graph ; Syntax.........: _SSLG_AddSample($iIdx, $iValue) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iValue - Value to add to graph Default advances graph without a data point displayed ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; - 3 Too many samples ~truncated ; - 4 Busy in another SSLG function ; Author ........: Bilgus ; Remarks .......: Adding too many samples between calls to UpdateGraph will cause samples to be lost ; =============================================================================================================================== Func _SSLG_AddSample($idSSLG, $iValue = Default) Local $iIdx = __SSLG_LookupHandle($idSSLG, True) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) Local $hPath = $g_SSLG[$iIdx][$gSSLG_hPathLine] Local $hPath_Grid = $g_SSLG[$iIdx][$gSSLG_hPathGridV] Local $aRect = $g_SSLG[$iIdx][$gSSLG_aRect] Local $iH = $aRect[3] Local $iY = $g_SSLG[$iIdx][$gSSLG_iY_Last] Local $iY_Last = $g_SSLG[$iIdx][$gSSLG_iY_Last] Local $iY_Min = $g_SSLG[$iIdx][$gSSLG_iY_Min] Local $iY_Max = $g_SSLG[$iIdx][$gSSLG_iY_Max] If $g_SSLG[$iIdx][$gSSLG_iY_Last] = Default And $iValue <> Default Then Return SetError(4, @extended, 0) Else $g_SSLG[$iIdx][$gSSLG_iY_Last] = Default ;Used as Mutex EndIf If $g_SSLG[$iIdx][$gSSLG_iSamples] <= 0 Then ; First sample, reset paths _GDIPlus_PathReset($hPath) _GDIPlus_PathReset($g_SSLG[$iIdx][$gSSLG_hPathGridV]) EndIf If $iValue <> Default Then If $iY_Min < $iY_Max Then $iY = $g_SSLG[$iIdx][$gSSLG_iY_Max] - $iValue ;Need to flip direction of Y Else $iY = $iValue EndIf If Not $iY_Last Then $iY_Last = $iY _GDIPlus_PathAddLine($hPath, $g_SSLG[$iIdx][$gSSLG_iSamples] - 1, $iY_Last, $g_SSLG[$iIdx][$gSSLG_iSamples], $iY) ;ConsoleWrite(StringFormat("AddLine (%d, %d) (%d, %d) \r\n", $g_SSLG[$iIdx][$gSSLG_iSamples] - 1, $iY_Last, $g_SSLG[$iIdx][$gSSLG_iSamples], $iY )) EndIf If $g_SSLG[$iIdx][$gSSLG_hPenGrid] And Mod($g_SSLG[$iIdx][$gSSLG_iSamplesTotal], $g_SSLG[$iIdx][$gSSLG_iX_Inc]) = 0 Then ;Make a new vertical grid line _GDIPlus_PathStartFigure($hPath_Grid) _GDIPlus_PathAddLine($hPath_Grid, $g_SSLG[$iIdx][$gSSLG_iSamples], $iY_Min, $g_SSLG[$iIdx][$gSSLG_iSamples], $iY_Max) _GDIPlus_PathCloseFigure($hPath_Grid) EndIf $g_SSLG[$iIdx][$gSSLG_iSamples] += 1 $g_SSLG[$iIdx][$gSSLG_iSamplesTotal] += 1 ;Used to track gridline increment $g_SSLG[$iIdx][$gSSLG_iY_Last] = $iY ;Release Mutex If $g_SSLG[$iIdx][$gSSLG_iSamples] > $g_SSLG[$iIdx][$gSSLG_iIncrements] Then Return SetError(3, @extended, 0) Return 1 EndFunc ;==>_SSLG_AddSample ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_UpdateGraph ; Description ...: Updates Line Graph with new values ; Syntax.........: _SSLG_UpdateGraph($iIdx, $iValue) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $bCompress - Compress end of graph rather than discard (slower) ; $bFill - Fill under the graph line with hBrushLine ; $bForce Update even though control is disabled ; Return values .: Success - 1 ; @Extended - Number of samples displayed ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; - 2 No Samples ; - 3 Samples overflow graph ; - 4 Busy in another SSLG function ; Author ........: Bilgus ; Remarks .......: Flip Y_Min and Y_Max in the call to CreateGraph to change fill area from under to above ; $bCompress = True uses more CPU and increases memory churn due to GDI+ temp objects ; =============================================================================================================================== Func _SSLG_UpdateGraph($idSSLG, $bCompress = False, $bFill = False, $bForce = False) Local $iIdx = __SSLG_LookupHandle($idSSLG, $bForce) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) Local $iErr = 0, $iY_Last If $g_SSLG[$iIdx][$gSSLG_bNeedResize] Then _SSLG_ClearGraph($idSSLG) Local $iSamples = $g_SSLG[$iIdx][$gSSLG_iSamples] If $iSamples < 1 Then Return SetError(2, @extended, 0) ElseIf $iSamples > $g_SSLG[$iIdx][$gSSLG_iIncrements] Then ;ConsoleWrite("!Full!" & @CRLF) $iSamples = $g_SSLG[$iIdx][$gSSLG_iIncrements] $iErr = 3 EndIf If $g_SSLG[$iIdx][$gSSLG_iY_Last] = Default Then SetError(4, @extended, 0) ; Currently in the AddSample or GraphClear Function Else $iY_Last = $g_SSLG[$iIdx][$gSSLG_iY_Last] $g_SSLG[$iIdx][$gSSLG_iY_Last] = Default ;Used as Mutex EndIf $g_SSLG[$iIdx][$gSSLG_iSamples] = 0 Local $hPath = $g_SSLG[$iIdx][$gSSLG_hPathLine] Local $hMatrixHT = $g_SSLG[$iIdx][$gSSLG_hMatrixHT] Local $aRect = $g_SSLG[$iIdx][$gSSLG_aRect] Local $iW = $aRect[2] Local $iH = $aRect[3] Local $iIncrement = $g_SSLG[$iIdx][$gSSLG_iIncrementSize] Local $iInc_Total = $iIncrement * $iSamples Local $iShiftDist = $iW - $iInc_Total ;ConsoleWrite(StringFormat("!Display (%d, %d; %d;) \r\n", $iSamples, $iInc_Total, $iShiftDist)) Local $iHistX = $iInc_Total Local $iHistW = $iShiftDist If $bCompress Then $iHistX = 0 $iHistW = $iW EndIf Local $aGraphic[2], $aBmp[2] Static Local $iLastBuf = 0 ;Swap back buffer each call Local $iBuf0 = $iLastBuf, $iBuf1 = (1 - $iLastBuf) ;Toggles 0,1 $aGraphic[$iBuf0] = $g_SSLG[$iIdx][$gSSLG_hGrBuf_0] $aGraphic[$iBuf1] = $g_SSLG[$iIdx][$gSSLG_hGrBuf_1] $aBmp[$iBuf0] = $g_SSLG[$iIdx][$gSSLG_hBitmap_0] $aBmp[$iBuf1] = $g_SSLG[$iIdx][$gSSLG_hBitmap_1] $iLastBuf = $iBuf1 ;Shift/Copy/Compress the last data to the current bitmap _GDIPlus_GraphicsDrawImageRectRect($aGraphic[0], $aBmp[1], _ $iHistX, 0, $iHistW, $iH, _ ;[Src XYWH] 0, 0, $iShiftDist, $iH) ; [Dst XYWH] ;Here we scale the path(points) to the size of the graph X increment and Y range _GDIPlus_MatrixSetElements($hMatrixHT, $iIncrement, 0, 0, (($iH - 1) / $g_SSLG[$iIdx][$gSSLG_iY_Range]), $iShiftDist, 0) If $bFill And _GDIPlus_PathGetPointCount($hPath) > 0 Then ;Create 3 lines to enclose desired fill area -- If $iY_Min was used it'd fill top of graph instead Local $iY_F = $g_SSLG[$iIdx][$gSSLG_iY_Max] Static Local $tPoints = DllStructCreate("float[" & 3 * 2 & "]") DllStructSetData($tPoints, 1, $iSamples, 1) DllStructSetData($tPoints, 1, $iY_Last, 2) DllStructSetData($tPoints, 1, $iSamples, 3) DllStructSetData($tPoints, 1, $iY_F, 4) DllStructSetData($tPoints, 1, -1, 5) DllStructSetData($tPoints, 1, $iY_F, 6) DllCall($__g_hGDIPDll, "int", "GdipAddPathLine2", "handle", $hPath, "struct*", $tPoints, "int", 3) ;_GDIPlus_PathSetFillMode ($hPath, 0) _GDIPlus_MatrixSetElements($hMatrixHT, $iIncrement, 0, 0, (($iH - 1) / $g_SSLG[$iIdx][$gSSLG_iY_Range]), $iShiftDist, 0) _GDIPlus_PathTransform($hPath, $hMatrixHT) _GDIPlus_GraphicsFillPath($aGraphic[0], $hPath, $g_SSLG[$iIdx][$gSSLG_hBrushLine]) _GDIPlus_GraphicsDrawPath($aGraphic[0], $hPath, $g_SSLG[$iIdx][$gSSLG_hPen]) Else _GDIPlus_MatrixSetElements($hMatrixHT, $iIncrement, 0, 0, (($iH - 1) / $g_SSLG[$iIdx][$gSSLG_iY_Range]), $iShiftDist, 0) _GDIPlus_PathTransform($hPath, $hMatrixHT) _GDIPlus_GraphicsDrawPath($aGraphic[0], $hPath, $g_SSLG[$iIdx][$gSSLG_hPen]) EndIf ;_GDIPlus_PathReset($hPath) (Now Handled in AddSample) If $g_SSLG[$iIdx][$gSSLG_hPenGrid] Then _GDIPlus_PathAddPath($g_SSLG[$iIdx][$gSSLG_hPathGridV], $g_SSLG[$iIdx][$gSSLG_hPathGridH], False) _GDIPlus_PathTransform($g_SSLG[$iIdx][$gSSLG_hPathGridV], $hMatrixHT) _GDIPlus_GraphicsDrawPath($aGraphic[0], $g_SSLG[$iIdx][$gSSLG_hPathGridV], $g_SSLG[$iIdx][$gSSLG_hPenGrid]) ;_GDIPlus_PathReset($g_SSLG[$iIdx][$gSSLG_hPathGridV]) (Now Handled in AddSample) EndIf If $g_SSLG[$iIdx][$gSSLG_hPenSamp] Then ;Place a line at the end of each sample period _GDIPlus_GraphicsDrawLine($aGraphic[0], $iW - 1, 0, $iW - 1, $iH, $g_SSLG[$iIdx][$gSSLG_hPenSamp]) EndIf __SSLG_GraphicsDraw($iIdx, $aBmp[0], $iW, $iH) ;Copy back to ctrl ;Clear Next Bitmap _GDIPlus_GraphicsFillRect($aGraphic[1], 0, 0, $iW, $iH, $g_SSLG[$iIdx][$gSSLG_hBrushBkgnd]) $g_SSLG[$iIdx][$gSSLG_iY_Last] = $iY_Last ;Release mutex Return SetError($iErr, $iSamples, 1) EndFunc ;==>_SSLG_UpdateGraph ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetSampleIndicator ; Description ...: Sets Line Color and line size of sample update line ; Syntax.........: _SSLG_SetSetSampleIndicator($iIdx, $iARGB, $iPenSize) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iLineColor - Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB) [Default] = False Removes Indicator ; $iPenSize - Size of pen [Default] = 2 ; $iPenStyle - GDIP_DASHSTYLE [Default] = $GDIP_DASHSTYLESOLID ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; - 2 Error setting pen color ; Author ........: Bilgus ; Remarks .......: none ; =============================================================================================================================== Func _SSLG_SetSetSampleIndicator($idSSLG, $iLineColor = False, $iPenSize = Default, $iPenStyle = Default) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) If $iPenStyle = Default Then $iPenStyle = $GDIP_DASHSTYLESOLID If $iLineColor = False Then _GDIPlus_PenDispose($g_SSLG[$iIdx][$gSSLG_hPenSamp]) $g_SSLG[$iIdx][$gSSLG_hPenSamp] = False Return 1 EndIf If $iPenStyle = Default Then $iPenStyle = $GDIP_DASHSTYLEDOT $iLineColor = __SSLG_RGB_ARGB($iLineColor) If Not $g_SSLG[$iIdx][$gSSLG_hPenSamp] And $iPenSize = Default Then $iPenSize = 1 If $iPenSize <> Default Then _GDIPlus_PenDispose($g_SSLG[$iIdx][$gSSLG_hPenSamp]) $g_SSLG[$iIdx][$gSSLG_hPenSamp] = _GDIPlus_PenCreate($iLineColor, $iPenSize) Else _GDIPlus_PenSetColor($g_SSLG[$iIdx][$gSSLG_hPenSamp], $iLineColor) If @error Then Return SetError(2, @extended, 0) EndIf _GDIPlus_PenSetDashStyle($g_SSLG[$iIdx][$gSSLG_hPenSamp], $iPenStyle) Return 1 EndFunc ;==>_SSLG_SetSetSampleIndicator ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetGrid ; Description ...: Sets Grid color x/y interval and line size of Grid ; Syntax.........: _SSLG_SetGrid($iIdx, $iX_c, $iY_c, $iARGB, $iPenSize) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iX_c = 0 - X axis grid lines (vertical) [Default] = 0 -- Removes Vertical ; $iY_c = 0 - Y axis grid lines (hoizontal) [Default] = 0 -- Removes Horizontal ; $iGridColor - Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB) ; $iPenSize - Size of pen [Default] = 2 ; $iPenStyle - GDIP_DASHSTYLE [Default] = ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; - 2 Error setting pen color ; Author ........: Bilgus ; Remarks .......: none ; =============================================================================================================================== Func _SSLG_SetGrid($idSSLG, $iX_c, $iY_c, $iGridColor = Default, $iPenSize = Default, $iPenStyle = Default) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) Local $aRect = $g_SSLG[$iIdx][$gSSLG_aRect] Local $iH = $aRect[3] If $iGridColor = Default Then $iGridColor = 0xFFA0A0A0 If $iPenSize = Default Then $iPenSize = 1 If $iPenStyle = Default Then $iPenStyle = $GDIP_DASHSTYLESOLID $iGridColor = __SSLG_RGB_ARGB($iGridColor) Local $hPath_Grid = $g_SSLG[$iIdx][$gSSLG_hPathGridH] _GDIPlus_PathReset($hPath_Grid) _GDIPlus_PenDispose($g_SSLG[$iIdx][$gSSLG_hPenGrid]) $g_SSLG[$iIdx][$gSSLG_hPenGrid] = False ;NOTE: notice the negative (-) sign on the Step value -- without it Step = 0 hangs For $j = $iH - 1 To 0 Step -Int($iY_c) _GDIPlus_PathStartFigure($hPath_Grid) _GDIPlus_PathAddLine($hPath_Grid, -1, $j, $g_SSLG[$iIdx][$gSSLG_iIncrements] - 1, $j) _GDIPlus_PathCloseFigure($hPath_Grid) Next $g_SSLG[$iIdx][$gSSLG_iX_Inc] = Int($iX_c) If $iX_c <> 0 Or $iY_c <> 0 Then $g_SSLG[$iIdx][$gSSLG_hPenGrid] = _GDIPlus_PenCreate($iGridColor, $iPenSize) _GDIPlus_PenSetDashStyle($g_SSLG[$iIdx][$gSSLG_hPenGrid], $iPenStyle) EndIf _SSLG_ClearGraph($idSSLG) Return 1 EndFunc ;==>_SSLG_SetGrid ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetLine ; Description ...: Sets Line Color and line size of Graph ; Syntax.........: _SSLG_SetLineColor($iIdx, $iARGB) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iLineColor - Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB) ; $iPenSize - Size of pen [Default] = 2 ; $iFillColor - Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB) [Default] = $iLineColor ; $hBrush - you may also supply your own brush to fill area under plotted line (deleted on exit by SSLG) ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; - 2 Error setting pen color ; Author ........: Bilgus ; Remarks .......: none ; =============================================================================================================================== Func _SSLG_SetLine($idSSLG, $iLineColor, $iPenSize = Default, $iFillColor = Default, $hBrush = Default) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) $iLineColor = __SSLG_RGB_ARGB($iLineColor) If $iFillColor = Default Then $iFillColor = $iLineColor $iFillColor = __SSLG_RGB_ARGB($iFillColor) _GDIPlus_BrushDispose($g_SSLG[$iIdx][$gSSLG_hBrushLine]) If $hBrush = Default Then $g_SSLG[$iIdx][$gSSLG_hBrushLine] = _GDIPlus_BrushCreateSolid($iFillColor) Else $g_SSLG[$iIdx][$gSSLG_hBrushLine] = $hBrush EndIf If Not $g_SSLG[$iIdx][$gSSLG_hPen] And $iPenSize = Default Then $iPenSize = 2 If $iPenSize <> Default Then _GDIPlus_PenDispose($g_SSLG[$iIdx][$gSSLG_hPen]) $g_SSLG[$iIdx][$gSSLG_hPen] = _GDIPlus_PenCreate($iLineColor, $iPenSize) EndIf GUICtrlSetColor($idSSLG, BitAND(BitNOT(0xFF000000), $iLineColor)) _GDIPlus_PenSetColor($g_SSLG[$iIdx][$gSSLG_hPen], $iLineColor) If @error Then Return SetError(2, @extended, 0) Return 1 EndFunc ;==>_SSLG_SetLine ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetSmoothingMode ; Description ...: Sets the graph object rendering quality ; Syntax.........: _SSLG_SetSmoothingMode($iIdx, $iSmooth = 2) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iSmooth - Smoothing mode ; 0 - Smoothing is not applied ; 1 - Smoothing is applied using an 8 X 4 box filter ; 2 - Smoothing is applied using an 8 X 8 box filter [Default] ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; Author ........: Beege ; Remarks .......: none ; =============================================================================================================================== Func _SSLG_SetSmoothingMode($idSSLG, $iSmooth = 0) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) _GDIPlus_GraphicsSetSmoothingMode($g_SSLG[$iIdx][$gSSLG_hGrBuf_0], $iSmooth) _GDIPlus_GraphicsSetSmoothingMode($g_SSLG[$iIdx][$gSSLG_hGrBuf_1], $iSmooth) Return 1 EndFunc ;==>_SSLG_SetSmoothingMode ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_ClearGraph ; Description ...: Clears all data from Graph ; Syntax.........: _SSLG_ClearGraph($iIdx) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; Author ........: Bilgus ; Remarks .......: none ; =============================================================================================================================== Func _SSLG_ClearGraph($idSSLG) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) If $g_SSLG[$iIdx][$gSSLG_bNeedResize] Then if __SSLG_GraphicsSetup($iIdx) = 0 Then Return EndIf $g_SSLG[$iIdx][$gSSLG_iY_Last] = Default ;Used as Mutex ;Need to clear both buffers we dont keep track which is current _GDIPlus_GraphicsClear($g_SSLG[$iIdx][$gSSLG_hGrBuf_0], $g_SSLG[$iIdx][$gSSLG_iBackColor]) ; _GDIPlus_GraphicsClear($g_SSLG[$iIdx][$gSSLG_hGrBuf_1], $g_SSLG[$iIdx][$gSSLG_iBackColor]) ; _GDIPlus_PathReset($g_SSLG[$iIdx][$gSSLG_hPathGridV]) _GDIPlus_PathReset($g_SSLG[$iIdx][$gSSLG_hPathLine]) $g_SSLG[$iIdx][$gSSLG_iSamplesTotal] = 0 $g_SSLG[$iIdx][$gSSLG_iSamples] = 0 If $g_SSLG[$iIdx][$gSSLG_hPenGrid] Then ;Add blank samples to draw grid For $i = 0 To $g_SSLG[$iIdx][$gSSLG_iIncrements] - 1 _SSLG_AddSample($idSSLG, Default) Next $g_SSLG[$iIdx][$gSSLG_iY_Last] = False _SSLG_UpdateGraph($idSSLG, False, False, True) EndIf $g_SSLG[$iIdx][$gSSLG_iY_Last] = False Return 1 EndFunc ;==>_SSLG_ClearGraph ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetResizeGraph ; Description ...: Resize Graph after call to GUICtrlSetPos ; Syntax.........: _SSLG_ResizeGraph($iIdx) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; Author ........: Bilgus ; Remarks .......: Only sets flag -- resize will occur on next ClearGraph or UpdateGraph ; =============================================================================================================================== Func _SSLG_SetResizeGraph($idSSLG) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) $g_SSLG[$iIdx][$gSSLG_bNeedResize] = True Return 1 EndFunc ;==>_SSLG_SetResizeGraph ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetYRange ; Description ...: Sets the Minimum and Maximum Y Values ; Syntax.........: _SSLG_SetYRange($iIdx, $iY_Min, $iY_Max) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iY_Min - Minimum Y Value ; $iY_Max - Maximum Y Value ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; Author ........: Beege ; Remarks .......: User should most likly want to ClearGraph after changing Y Range ; =============================================================================================================================== Func _SSLG_SetYRange($idSSLG, $iY_Min, $iY_Max) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) $g_SSLG[$iIdx][$gSSLG_iY_Min] = $iY_Min $g_SSLG[$iIdx][$gSSLG_iY_Max] = $iY_Max $g_SSLG[$iIdx][$gSSLG_iY_Range] = Abs($iY_Max - $iY_Min) Return 1 EndFunc ;==>_SSLG_SetYRange ; #FUNCTION# ==================================================================================================================== ; Name...........: _SSLG_SetBackGroundColor ; Description ...: Sets Graph BackGround Color ; Syntax.........: _SSLG_SetBackGroundColor($iIdx, $iARGB = 0xFF000000) ; Parameters ....: $idSSLG - Control ID returned from _SSLG_CreateGraph() ; $iARGB - Alpha, Red, Green and Blue Hex Value. (0xAARRGGBB). Default = Black ; Return values .: Success - 1 ; Failure - 0 and sets @ERROR: ; - 1 Invalid iIndex ; Author ........: Beege ; Remarks .......: All previous data will be cleared ; =============================================================================================================================== Func _SSLG_SetBackGroundColor($idSSLG, $iARGB = 0xFF000000) Local $iIdx = __SSLG_LookupHandle($idSSLG) If $iIdx > $g_SSLG[0][0] Then Return SetError(1, @extended, 0) $g_SSLG[$iIdx][$gSSLG_iBackColor] = __SSLG_RGB_ARGB($iARGB) _GDIPlus_BrushDispose($g_SSLG[$iIdx][$gSSLG_hBrushBkgnd]) $g_SSLG[$iIdx][$gSSLG_hBrushBkgnd] = _GDIPlus_BrushCreateSolid($g_SSLG[$iIdx][$gSSLG_iBackColor]) _SSLG_ClearGraph($iIdx) Return 1 EndFunc ;==>_SSLG_SetBackGroundColor #EndRegion Public Functions #Region Internal Functions ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __SSLG_RGB_ARGB ; Description ...: Convert RBG color to ARGB color ; Syntax.........: ; Parameters ....: ; Return values .: ; Author ........: Bilgus ; Remarks .......: ; =============================================================================================================================== Func __SSLG_RGB_ARGB($iARGB) If BitAND($iARGB, 0xFF000000) = 0 Then $iARGB = BitOR($iARGB, 0xFF000000) EndIf Return $iARGB EndFunc ;==>__SSLG_RGB_ARGB ; #INTERNAL_USE_ONLY# ==================================================================================================================== ; Name...........: __SSLG_GraphicsSetup ; Description ...: Internal function -- (Re)Creates all the graphics objects ; Syntax.........: __SSLG_GraphicsSetup($iIdx) ; Parameters ....: $iIdx - INTERNAL Graph ID ; Return values .: N/A ; Failure - 0 and sets @ERROR: ; - 2 No change ; Author ........: Bilgus ; Remarks .......: All previous data will be cleared, Graphics objects destroyed, Re-created if $bReInit = True ; =============================================================================================================================== Func __SSLG_GraphicsSetup($iIdx) $g_SSLG[$iIdx][$gSSLG_bNeedResize] = False Local $aRectOld = $g_SSLG[$iIdx][$gSSLG_aRect] Local $aRect = WinGetPos($g_SSLG[$iIdx][$gSSLG_hCtrl]) If $g_SSLG[$iIdx][$gSSLG_hBitmap_0] And $aRect[2] = $aRectOld[2] And $aRect[3] = $aRectOld[3] Then ;ConsoleWrite("Resize not needed") Return SetError(2, @extended, 0) EndIf $g_SSLG[$iIdx][$gSSLG_aRect] = $aRect Local $iW = $aRect[2] Local $iH = $aRect[3] ;ConsoleWrite("New SZ: (" & $aRect[0] & ", " & $aRect[1] & "), (" & $iW & ", " & $iH & ")" & @CRLF) Local $hHBMP, $hGrBuf0, $hGrBuf1, $hBitmap0, $hBitmap1 If $g_SSLG[$iIdx][$gSSLG_hBitmap_0] Then $hHBMP = $g_SSLG[$iIdx][$gSSLG_hHBitmapGDI_] $hGrBuf0 = $g_SSLG[$iIdx][$gSSLG_hGrBuf_0] $hBitmap0 = $g_SSLG[$iIdx][$gSSLG_hBitmap_0] $hGrBuf1 = $g_SSLG[$iIdx][$gSSLG_hGrBuf_1] $hBitmap1 = $g_SSLG[$iIdx][$gSSLG_hBitmap_1] $g_SSLG[$iIdx][$gSSLG_iIncrementSize] = -1 EndIf Local $hGraphic = _GDIPlus_GraphicsCreateFromHWND($g_SSLG[$iIdx][$gSSLG_hCtrl]) Local $hBitmap = _GDIPlus_BitmapCreateFromGraphics($iW, $iH, $hGraphic) ;$GDIP_PXF32PARGB (Pre-multiplied) appears to be the most efficient format as far as GDI+ is concerned $g_SSLG[$iIdx][$gSSLG_hBitmap_0] = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iW, $iH, $GDIP_PXF32PARGB) $g_SSLG[$iIdx][$gSSLG_hGrBuf_0] = _GDIPlus_ImageGetGraphicsContext($g_SSLG[$iIdx][$gSSLG_hBitmap_0]) $g_SSLG[$iIdx][$gSSLG_hBitmap_1] = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iW, $iH, $GDIP_PXF32PARGB) $g_SSLG[$iIdx][$gSSLG_hGrBuf_1] = _GDIPlus_ImageGetGraphicsContext($g_SSLG[$iIdx][$gSSLG_hBitmap_1]) $g_SSLG[$iIdx][$gSSLG_hHBitmapGDI_] = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) $g_SSLG[$iIdx][$gSSLG_iIncrementSize] = Ceiling($iW / ($g_SSLG[$iIdx][$gSSLG_iIncrements] + 1)) $g_SSLG[$iIdx][$gSSLG_iIncrements] = Int($iW / $g_SSLG[$iIdx][$gSSLG_iIncrementSize]) _GDIPlus_BitmapDispose($hBitmap) _GDIPlus_GraphicsDispose($hGraphic) Local $aResult = DllCall("user32.dll", "hwnd", "GetParent", "hwnd", $g_SSLG[$iIdx][$gSSLG_hCtrl]) If Not @error Then _WinAPI_RedrawWindow($aResult[0]) _WinAPI_DeleteObject($hHBMP) _GDIPlus_GraphicsDispose($hGrBuf0) _GDIPlus_BitmapDispose($hBitmap0) _GDIPlus_GraphicsDispose($hGrBuf1) _GDIPlus_BitmapDispose($hBitmap1) Return 1 EndFunc ;==>__SSLG_GraphicsSetup ; #INTERNAL_USE_ONLY# ==================================================================================================================== ; Name...........: __SSLG_GraphicsDraw ; Description ...: Internal function -- BitBlt back to hDC ; Syntax.........: __SSLG_GraphicsDraw($iIdx) ; Parameters ....: $iIdx - INTERNAL Graph ID ; $iW - Width ; $iH - Height ; Return values .: N/A ; Author ........: Bilgus ; Remarks .......: Copy the GDI+ bitmap to a HBITMAP blit to the screen ; =============================================================================================================================== Func __SSLG_GraphicsDraw($iIdx, $hBitmap, $iW, $iH) Local $hObjOld, $hDC_Src, $hDC_Dst, $hHBitmap, $hGraphic ;$hBitmap = GDI+ Bitmap [SRC] $hHBitmap = $g_SSLG[$iIdx][$gSSLG_hHBitmapGDI_] ;Standard GDI Bitmap [DEST] ;DC where we can select the Standard GDI Bitmap $hDC_Src = $g_SSLG[$iIdx][$gSSLG_hDCBuf] $hObjOld = _WinAPI_SelectObject($hDC_Src, $hHBitmap) $hGraphic = _GDIPlus_GraphicsCreateFromHDC($hDC_Src) _GDIPlus_GraphicsSetSmoothingMode($hGraphic, 2) _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0, $iW, $iH) _GDIPlus_GraphicsDispose($hGraphic) $hDC_Dst = _WinAPI_GetDC($g_SSLG[$iIdx][$gSSLG_hCtrl]) _WinAPI_BitBlt($hDC_Dst, 0, 0, $iW, $iH, $hDC_Src, 0, 0, $SRCCOPY) ;Copy back to ctrl _WinAPI_ReleaseDC($g_SSLG[$iIdx][$gSSLG_hCtrl], $hDC_Dst) _WinAPI_SelectObject($hDC_Src, $hObjOld) ;Put old object back EndFunc ;==>__SSLG_GraphicsDraw ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __SSLG_LookupHandle ; Description ...: Converts controlID or Handles to internal graph index ; Syntax.........: ; Parameters ....: ; Return values .: ; Author ........: Bilgus ; Remarks .......: ; =============================================================================================================================== Func __SSLG_LookupHandle($idSSLG, $bForce = True) Static Local $idSSLG_Last = -1 Static Local $hSSLG_Last = 0 Local Const $GUI_DISABLE = 128 If $bForce Or Not BitAND(GUICtrlGetState($idSSLG), $GUI_DISABLE) Then If $idSSLG = $idSSLG_Last Then Return $hSSLG_Last ;Shortcut $idSSLG = Hex($idSSLG) Local $oDict = $g_SSLG[0][$gSSLG_DICT] If $oDict.Exists($idSSLG) Then $idSSLG_Last = $idSSLG $hSSLG_Last = $oDict.Item($idSSLG) Return $hSSLG_Last EndIf ConsoleWriteError("Graph ControlID: " & $idSSLG & " does not exist" & @CRLF) For $vKey In $oDict ConsoleWriteError("[" & $vKey & "] = " & $oDict.Item($vKey) & @CRLF) Next EndIf Return SetError(1, @extended, $g_SSLG[0][0] + 1) EndFunc ;==>__SSLG_LookupHandle ; #INTERNAL_USE_ONLY# =========================================================================================================== ; Name...........: __SSLG_Exit() ; Description ...: Cleans up stored objects ; Syntax.........: ; Parameters ....: ; Return values .: ; Author ........: ; Remarks .......: ; =============================================================================================================================== Func __SSLG_Exit() If $g_SSLG[0][0] Then For $i = 1 To $g_SSLG[0][0] $g_SSLG[$i][$gSSLG_iY_Last] = Default ;Mutex _WinAPI_DeleteObject($g_SSLG[$i][$gSSLG_hHBitmapGDI_]) _GDIPlus_GraphicsDispose($g_SSLG[$i][$gSSLG_hGrBuf_1]) _GDIPlus_BitmapDispose($g_SSLG[$i][$gSSLG_hBitmap_1]) _GDIPlus_GraphicsDispose($g_SSLG[$i][$gSSLG_hGrBuf_0]) _GDIPlus_BitmapDispose($g_SSLG[$i][$gSSLG_hBitmap_0]) _GDIPlus_MatrixDispose($g_SSLG[$i][$gSSLG_hMatrixHT]) _GDIPlus_PathDispose($g_SSLG[$i][$gSSLG_hPathLine]) _GDIPlus_PathDispose($g_SSLG[$i][$gSSLG_hPathGridV]) _GDIPlus_PathDispose($g_SSLG[$i][$gSSLG_hPathGridH]) _GDIPlus_PenDispose($g_SSLG[$i][$gSSLG_hPenGrid]) _GDIPlus_PenDispose($g_SSLG[$i][$gSSLG_hPenSamp]) _GDIPlus_PenDispose($g_SSLG[$i][$gSSLG_hPen]) _GDIPlus_BrushDispose($g_SSLG[$i][$gSSLG_hBrushLine]) _GDIPlus_BrushDispose($g_SSLG[$i][$gSSLG_hBrushBkgnd]) _WinAPI_DeleteDC($g_SSLG[$i][$gSSLG_hDCBuf]) Next EndIf _GDIPlus_Shutdown() EndFunc ;==>__SSLG_Exit #EndRegion Internal Functions Updated: Previous Downloads [38 / 38/ 0] SSLG.au3Fetching info... waveform.au3Fetching info... Peak.au3Fetching info... Edited May 16, 2020 by Bilgus Add screenshot, Update Code, Update Code, Update Code Zmy and argumentum 2
Bilgus Posted April 29, 2020 Author Posted April 29, 2020 (edited) Since I have this extra post here I'll go into some of the details As you recall (or maybe you have no idea) Begee's original code only updated one point on each graph redraw This lead to some pretty high CPU usage if you wanted to add lots of points My idea was to allow adding multiple points before redrawing the graph....... Reveal hidden contents First Try: The first try I just encoded all the new points as 4byte chunks Concatenated in a binary string Then on redraw decode these new points and display them, this worked decently but was still a little slow Next Idea: My next idea was to use a graphics path and just add points at every sample (X, Y) (and the grid lines too) (X is the incremented sample position, Y the value position) (0, Y) (1, Y) (2, Y) (3, Y) (4, Y) This worked VERY well and also allowed me to get rid of the image shifting Bummer: I couldnt find a way to QUICKLY & easily discard the portion of the path that had scrolled off screen (except to walk the points and copy the ones I wanted to save to a new path) as a result CPU usage started going UP and UP over time ( since more and more points needed translation) Final Idea: So clearly a graphics path is an excellent way to do this Rather than continually adding to it instead I put the scrolling code back in but this time I only save the path till the redraw occurs On GraphUpdate The old graph is scrolled by (incrementSz * #Samples) and copied to the backbuffer The now blank right portion gets the background copied into the empty space (if enabled) the grid gets copied here too The new points are translated in the X direction (scaled as a function of (IncrementSz * SamplePos) I wanted to do this with Y as well but I seem to get a rotation instead of scaling The path gets reset back to empty and the Sample Count set to 0 Speaking of Sample Count GDI+ has a function to return number of points in a path (BUT it sometimes returns 0???) After translation the path is written to the backbuffer (if enabled) a vertical line is also placed at the right most portion of the graph to show distance between graph updates Finally the new image is copied back to the graph As I mentioned above I also return a real control Id from the create function This is accomplished by creating a label control and drawing on that instead of the main hWnd Having a label control underneath allows a few things Click Events Colored background Control Disable Real Control IDs Message displayed on disable Should also allow resizing the control though I didn't add this functionality as I have no need The only problem is how do we associate the control ID with the graph instance Here I used the Scripting.Dictionary object as a simple associative array the Key = Hex(controlID) Value = graph instance Note: setting the backcolor of the control doesn't change the graph background only the disabled background Oh hey if you look close at the code there is even a 'mutex' in there I noticed if the graph updates while in the add sample code the points go to the wrong sample when it returns Edited May 16, 2020 by Bilgus updates Zmy 1
Bilgus Posted May 2, 2020 Author Posted May 2, 2020 (edited) Code In first post updated Grids are now scaled to proper size Got rid of CloneBitmap on every graphics update (far less page faults now) Allow use of ARGB for colors or RGB Faster redraw (~10-20 ms) Y coords are now scaled using GDI+ Option to fill bottom portion of graph - Fixed scaling issue Edited May 2, 2020 by Bilgus
Bilgus Posted May 4, 2020 Author Posted May 4, 2020 I noticed that my control flickers a lot more than his original I removed the BitBlt calls in favor of the GDI+ DrawImage so I could get rid of creating a new HBitmap each update Looks to me like BitBlt does a better job I'll upload a version fixing this issue and still without creating a new HBitmap in a day or two Why am I trying to keep from creating a new bitmap each update you ask? -- PageFaults -- While they are soft pagefaults it still means the program is churning memory so I want to try and decrease as much as possible The other advantage is lower memory and processor usage Oh I also have added the ability to resize the control now It just deletes the old buffers and recreates them but it works
Bilgus Posted May 14, 2020 Author Posted May 14, 2020 SSLG code has been updated Turns out BitBlt is faster than _GDIPlus_DrawImage even after you do an extra copy to a GDI HBitmap Weird, Never would have guessed... BitBlt used for final drawing to control Added ability to resize the graph on window resize (try it in the example) SSLG is much more well behaved after this update There are now two buffers that get swapped every other graph update this gets us a free copy of the last image Grid lines resize automatically on control resize All sample scaling now handled with graphics path GDI+ has a lot of heap allocated memory behind the scenes about the only thing that still has a lot of allocations in SSLG is when you enable bCompress for the old samples otherwise you should see very few page faults
Bilgus Posted May 16, 2020 Author Posted May 16, 2020 (edited) Well I added another example peak.au3 it uses the Windows sound AudioMeterInfo.GetPeakValue code and displays it in a SSLG graph See for Trancex's original code: https://www.autoitscript.com/forum/topic/142523-master-loudnessvolume-via-peak-meter-windows-7-64-bit/?tab=comments#comment-1003940 Unfortunately I found a few more bugs which are (fortunately) now fixed AddSample did not check if UpdateGraph was currently running causing the graph to be cleared If Grid was enabled SetResizeGraph did not draw new gridlines after the resize Couple of error numbers were changed to match between AddSample and UpdateGraph Increments are now recalculated after IncrementsSize is calculated Edited May 16, 2020 by Bilgus argumentum and ptrex 2
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