LegitStack

ScreenCapture 4k res - how to get dpi awareness?

6 posts in this topic

#1 ·  Posted (edited)

Been struggling with this one for a while. 

when I do a _screencapture_capture call on a high resolution monitor (like my surface book) it gives me an image that has 2 problems:

1. its in the wrong location on the screen

and

2. it gives me a picture that is larger than the area of the screen I selected, though it only has the content of what I selected. 

---------------------------

I was able to easily fix problem #1 by manually adjusting the x y coordinates to compensate for the amount of DPI scale I have.

for instance if I'm 200% zoomed in the code looks like this:

Local $bmp = _ScreenCapture_Capture("", $iX1*2, $iY1*2, $iX2*2, $iY2*2, false)

 it's problem #2 that is the big problem. I'd like to attach a screen shot of what I'm talking about (see capture.png)

Capture.PNG

---------------------------

Now I basically understand why this is happening. ScreenCapture grabs each pixel of the screen. This screen, being a high resolution, when its zoomed in adds up several pixels to make one on the screen. 

This is a problem for me because I'm taking images of the screen and later looking for those exact images on the screen. if everything is blown up by an indeterminate amount (in my case 2x) then those images can't be found later on. 

Does anyone know what I can do? 

I tried resizing the images back down to no avail. 

_GDIPlus_ImageResize and _GDIPlus_ImageScale don't work because they don't compress the pixels correctly. quality is lost. and the exact image isn't preserved, so I can't search for it later. (see capture1.png)

Capture1.PNG

 

Anyway, I'm about to give up, been on this problem for too long! does anyone know what I can do? Seems to me that the ideal solution would be to eventually have autoit add an argument to _screencapture_capture that lets you specify a DPI scale amount or something.  that can be pulled from the registry at HKEY_CURRENT_USER\control panel\desktop\windowmetrics\appliedDPI.

But in the meantime, does anyone have any suggestions for how I can make my program compatible with 4k resolution monitors? I either need to take the screen capture like normal, then scale it down appropriately without losing quality, or I need to capture the screen in the first place like the human sees it. But I don't know how to do that either. 

I'll post my relevant code here: (in my project I call ScreenCapture_DPI_Aware)

#include <Security.au3>

Func _GetAppliedDPI()
  Local $aArrayOfData = _Security__LookupAccountName(@UserName)

  If IsArray($aArrayOfData) Then
    ;msgbox(64, "SID String = ", $aArrayOfData[0] & @CRLF)
    ;msgbox(64, "Domain name = ", $aArrayOfData[1] & @CRLF)
    ;msgbox(64, "SID type = ", _Security__SidTypeStr($aArrayOfData[2]) & @CRLF)
    ;Local $AppliedDPI = RegRead("HKEY_USERS\" & $aArrayOfData[0] & "\Control Panel\Desktop\WindowMetrics", "AppliedDPI")
    Local $AppliedDPI = RegRead("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics", "AppliedDPI")
    return $AppliedDPI
  EndIf
EndFunc



Func GetScale()
  $applied = _GetAppliedDPI()
  if $applied == "" then
    return 1
  else
    return $applied / 96
  EndIf
EndFunc



Func ScreenCapture_Capture_DPI_Aware($sBMP_Path, $iX1, $iY1, $iX2, $iY2, $bool)
  $R = GetScale() ;Raito

  Local $bmp = _ScreenCapture_Capture($sBMP_Path, $iX1*$R, $iY1*$R, $iX2*$R, $iY2*$R, $bool)

  ;Scaling didn't work:
  ;_ScaleImage($bmp, $sBMP_Path, abs($iX2 - $iX1), abs($iY2 - $iY1), $R)
  ;return _ScreenCapture_Capture($sBMP_Path, $iX1*$R, $iY1*$R, $iX2*$R, $iY2*$R, $bool)
  
EndFunc







;Func _ScaleImage($bmp, $outimage, $w, $h, $scale)

;  _GDIPlus_Startup()
    ;Get the encoder of to save the resized image in the format you want.
;    Local $Ext = StringUpper(StringMid($outimage, StringInStr($outimage, ".", 0, -1) + 1))
;    $CLSID = _GDIPlus_EncodersGetCLSID($Ext)
    ; code found here : https://www.autoitscript.com/autoit3/docs/libfunctions/_GDIPlus_ImageSaveToStream.htm
;    Local $sImgCLSID = _GDIPlus_EncodersGetCLSID("png") ;create CLSID for a JPG image file type
;    Local $tGUID = _WinAPI_GUIDFromString($sImgCLSID) ;convert CLSID GUID to binary form and returns $tagGUID structure
;    Local $tParams = _GDIPlus_ParamInit(1) ;initialize an encoder parameter list and return $tagGDIPENCODERPARAMS structure
;    Local $tData = DllStructCreate("int Quality") ;create struct to set JPG quality setting
;    DllStructSetData($tData, "Quality", 100) ;quality 0-100 (0: lowest, 100: highest)
;    Local $pData = DllStructGetPtr($tData) ;get pointer from quality struct
;    _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData) ;add a value to an encoder parameter list

;    Local $gbmp = _GDIPlus_BitmapCreateFromHBITMAP($bmp)
;    _WinAPI_DeleteObject($bmp)

;    Local $gsbmp = _GDIPlus_ImageResize($gbmp, $w * $scale, $h * $scale)
    ;Local $ext = _GDIPlus_EncodersGetCLSID("PNG")
;    _GDIPlus_ImageSaveToFileEx($gsbmp, $outimage, $sImgCLSID)

;    _GDIPlus_BitmapDispose($gbmp)
;    _GDIPlus_BitmapDispose($gsbmp)
;  _GDIPlus_Shutdown()
;EndFunc


Thanks for any help you can give me!!!

Edited by LegitStack

Share this post


Link to post
Share on other sites



2 hours ago, RTFC said:

I'm having a deja-lu:blink:

Thanks RTFC!

this is what I came up with to fix the prob:

#include <lib\applieddpi.au3>


Func Load_ScreenCapture_High_DPI_Check()
  $R = GetScale()
  if $R == 1 then
    ; should fix this to work with dual monitors. later.
  else
    #Region ;**** Directives created by AutoIt3Wrapper_GUI ****
    #AutoIt3Wrapper_Res_HiDpi=Y
    #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
    DllCall("User32.dll","bool","SetProcessDPIAware")
    ;(work around) first screencapture doesn't seem to work right, so take one and throw away.
    _ScreenCapture_Capture("",0,0,@DesktopWidth,@DesktopHeight)
  endif
EndFunc

Load_ScreenCapture_High_DPI_Check()

;;;;;;;AppliedDPI.au3


#include <Security.au3>


Func _GetAppliedDPI()
  Local $AppliedDPI = RegRead("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics", "AppliedDPI")
  return $AppliedDPI
EndFunc


Func GetScale()
  $applied = _GetAppliedDPI()
  if $applied == "" then
    return 1
  else
    return $applied / 96
  EndIf
EndFunc

 

 

Share this post


Link to post
Share on other sites
On 10/16/2016 at 2:36 PM, LegitStack said:

Thanks RTFC!

this is what I came up with to fix the prob:

#include <lib\applieddpi.au3>


Func Load_ScreenCapture_High_DPI_Check()
  $R = GetScale()
  if $R == 1 then
    ; should fix this to work with dual monitors. later.
  else
    #Region ;**** Directives created by AutoIt3Wrapper_GUI ****
    #AutoIt3Wrapper_Res_HiDpi=Y
    #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
    DllCall("User32.dll","bool","SetProcessDPIAware")
    ;(work around) first screencapture doesn't seem to work right, so take one and throw away.
    _ScreenCapture_Capture("",0,0,@DesktopWidth,@DesktopHeight)
  endif
EndFunc

Load_ScreenCapture_High_DPI_Check()

;;;;;;;AppliedDPI.au3


#include <Security.au3>


Func _GetAppliedDPI()
  Local $AppliedDPI = RegRead("HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics", "AppliedDPI")
  return $AppliedDPI
EndFunc


Func GetScale()
  $applied = _GetAppliedDPI()
  if $applied == "" then
    return 1
  else
    return $applied / 96
  EndIf
EndFunc

 

 

New to using computers with High Resolution screens...    and trying to get my scripts to adjust similar problems to this thread.  I'm having difficulty getting autoit to locate 

#include <lib\applieddpi.au3>

what file or location would I have it search for that?  thank you for entertaining a noob question... never had issues before  guessing the script itself isnt saved in the right location to find the file. 

 

 

Share this post


Link to post
Share on other sites

@JennMaughan: AFAIK, that's not a standard AutoIt include file, but probably something user LegitStack put together. You should be alright just copying only those functions to your own script(s), if you wish to use them.:)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Similar Content

    • vikashbitm2010
      By vikashbitm2010
      Hi all,
      I want to return string from autoit script to the python code.
      Autoit:
      Exit("22 FE 45") -> not working
      Exit(1) -> Working
      Exit(0) -> Working
      python:
      import os
      res=os.system(autoitmine.exe)
      print "res",res
      I got some hint of @ExitCode macro, but dont know how to use it to get the string in python variable 'res'.
      Please resolve this issue for me if anyone knows about this.
       
    • QuickWhiteWolf
      By QuickWhiteWolf
      Hi everyone,
       
      I was previously a member of this forum under the username Wombat. It's been years and multiple email accounts closed since then so I decided to start fresh and take a moment to thank you all... (Admins/Mods let me know if we need to discuss this...)
       
      I started programming with AutoIT while working as a scrap catcher for a machine that chopped scrap into pieces for easier moving, I learned styles and gained strengths from some of the best members on this forum by reverse engineering their code. I gained the confidence of our IT manager by making a boast that I could write an application to replace a p.o.s. cobalt based app we were using on the floor at that time, needless to say I was way in over my head but he saw that I had potential and I luckily had built several other apps on the side that were of equal or greater value to the company. I've been working as help-desk for the past 3 years and writing software as well to facilitate the help desk and solve recurring issues with our users. I was given an office and moved out of help-desk about a year ago, after 5 years of hard work I've actually landed the title of Jr. Developer moving into mid level title/pay this year! The company has already set out an improvement path that sees me with 4 certs and a bachelors in 4 more years making great money. Before this I had only ever worked at gas stations, fast food and manual labor jobs. If you're ever worried about your life, want something more, or just want a change you can do it. It's not easy, not at all, but it's possible and software programming is a very rewarding field if you like to make things and see how others interact with them.

      I utilized AutoIT to bring a company into the twenty-first century, away from paper trails and sticky notes improving the quality of life for the employees on the floor (where I started before learning AutoIT). I was given the go ahead to purchase visual studio and I learned VB.Net and built an awesome piece of Zebra labeling software (Utilizing ZPL code translated froma graphical editor) for our shipping department. Now I'm diving head first into C# and we have another programmer on board as we move on to MS Team Services and begin to tackle a sweet new project involving real time awareness of our product on the factory floor utilizing RFID and windows 10 tablets.

      That's a long way to come in just 4 years, and I couldn't have done it without the gigantic heart this community has and the mentorship provided for people looking to get into programming.

      So from the bottom of my heart, with immense respect.... Thank you so much AutoIT community
    • Valnurat
      By Valnurat
      Hi.
      How do you find all printers installed on a computer?
      I'm searching for the name, location and port
       
    • anub13
      By anub13
      Hi,

      I am having difficulties wondering why my code bugging like this,
      the symptom : I have a form with combo box, and details in bottom of the app, when user choose another option the details changes immediately but when I go to details, it goes up and down endlessly
      here is the code
       
      Global $cChkPatrol, $YES= "YES", $NO = "NO" if GUICtrlRead($combo1) = "YES" Then $cChkPatrol = $YES Else $cChkPatrol = $NO EndIf func StartForm() GUICtrlSetData($txtInfo,"ADDITIONAL INFORMATION THAT WILL BE SENT WITH THE MESSAGE:" & @CRLF & asdfgSystemInfo()) GUISetState(@SW_SHOW) AdlibRegister("_CheckPatrol") While 1 $nMsg = GUIGetMsg() Select case $nMsg = $txtEmailAddress GUICtrlSetData($txtInfo,"ADDITIONAL INFORMATION THAT WILL BE SENT WITH THE MESSAGE:" & @CRLF & asdfgSystemInfo()) case $nMsg = $txtPhone GUICtrlSetData($txtInfo,"ADDITIONAL INFORMATION THAT WILL BE SENT WITH THE MESSAGE:" & @CRLF & asdfgSystemInfo()) case $nMsg = $txtFullName GUICtrlSetData($txtInfo,"ADDITIONAL INFORMATION THAT WILL BE SENT WITH THE MESSAGE:" & @CRLF & asdfgSystemInfo()) case $nMsg = $cChkPatrol GUICtrlSetData($txtInfo,"ADDITIONAL INFORMATION THAT WILL BE SENT WITH THE MESSAGE:" & @CRLF & asdfgSystemInfo()) case $nMsg = $cmdScreenShot $gScreenShotFile = SaveScreenShot() case $nMsg = $cmdSubmit if ValidateForm() then if asdfgSubmit() Then UpdateStatus("Your request has been sent successfully") MsgBox(0,"Help Requested","Your request has been sent successfully") ExitLoop Else MsgBox(0,"Help Failed","Something is wrong you request was not sent, please try again") Endif endIf case $nMsg = $cmdAttach $gAttachFile = AttachFile() Case $nMsg = $GUI_EVENT_CLOSE ExitLoop EndSelect WEnd AdlibUnRegister("_CheckPatrol") EndFunc func asdfgSystemInfo() dim $asdfgInfo $asdfgInfo = "" & @CRLF if $isAdminVersion=1 then $asdfgInfo = $asdfgInfo & @CRLF & " Ticket Tags:" & @CRLF $asdfgInfo = $asdfgInfo & "~#userphone='" & GUICtrlRead($txtPhone) & "'" & @CRLF $asdfgInfo = $asdfgInfo & "~#submitterphone='" & GUICtrlRead($txtPhone) & "'" & @CRLF dim $MachineID $MachineID = KaseyaIni($gKIniFile,"SERVER COMMUNICATIONS","User_Name","NotFound") $asdfgInfo = $asdfgInfo & "~#machineid='" & $MachineID & "'" & @CRLF dim $OrgGroups $OrgGroups= StringSplit($MachineID,".") dim $OrgGroupIndex $OrgGroupIndex = UBound($OrgGroups) - 1 $asdfgInfo = $asdfgInfo & "~#organization='" & $OrgGroups[$OrgGroupIndex] & "'" & @CRLF dim $EmailUser $EmailUser = GUICtrlRead($txtEmailAddress) $EmailUser = StringMid($EmailUser,1,StringInStr($EmailUser,"@") - 1) & @CRLF & @CRLF $asdfgInfo = $asdfgInfo & "~#username='" & $EmailUser & "'" & @CRLF ;dim ;Call("isChecked") ;$categ = GUICtrlRead($chkPatrol) ;$asdfgInfo = $asdfgInfo & "-#category='" & $categ & "'" & @CRLF EndIf $asdfgInfo = $asdfgInfo & "Email:" & GUICtrlRead($txtEmailAddress) & @CRLF & @CRLF $asdfgInfo = $asdfgInfo & "Full Name:" & GUICtrlRead($txtFullName) & @CRLF & @CRLF $asdfgInfo = $asdfgInfo & "Phone:" & GUICtrlRead($txtPhone) & @CRLF & @CRLF $asdfgInfo = $asdfgInfo & "DateTime:" & _Now() & @CRLF $asdfgInfo = $asdfgInfo & "MachineName:" & @ComputerName & @CRLF $asdfgInfo = $asdfgInfo & "UserName:" & @UserName & @CRLF $asdfgInfo = $asdfgInfo & "OS:" & @OSType & " " & @OSVersion & @CRLF $asdfgInfo = $asdfgInfo & "LogonDomain:" & @OSType & " " & @LogonDomain & @CRLF $asdfgInfo = $asdfgInfo & "LogonDNSDomain:" & @OSType & " " & @LogonDNSDomain & @CRLF $asdfgInfo = $asdfgInfo & "OS:" & @OSType & " " & @OSVersion & @CRLF $asdfgInfo = $asdfgInfo & "IP Address:" & @IPAddress1 & @CRLF $asdfgInfo = $asdfgInfo & "Public IP Address:" & GetPublicIP() &@CRLF ;$asdfgInfo = $asdfgInfo & "Public IP Address:" & _GetIP () &@CRLF $asdfgInfo = $asdfgInfo & "OS:" & NetAdapterInfo() $asdfgInfo = $asdfgInfo & "KaseyaInstalled:" & KaseyaInstalled() & @CRLF $asdfgInfo = $asdfgInfo & "KaseyaRunning:" & KaseyaRunning() & @CRLF $asdfgInfo = $asdfgInfo & "KaseyaAgentGUID:" & KaseyaIni($gKIniFile,"SERVER COMMUNICATIONS","Agent_Guid","NotFound") & @CRLF $asdfgInfo = $asdfgInfo & "asdfgPaid:" & isasdfgIDPaid() & @CRLF if $isAdminVersion=0 then $asdfgInfo = $asdfgInfo & @CRLF & " Ticket Tags:" & @CRLF $asdfgInfo = $asdfgInfo & "~userphone='" & GUICtrlRead($txtPhone) & "'" & @CRLF $asdfgInfo = $asdfgInfo & "~submitterphone='" & GUICtrlRead($txtPhone) & "'" & @CRLF dim $MachineID $MachineID = KaseyaIni($gKIniFile,"SERVER COMMUNICATIONS","User_Name","NotFound") $asdfgInfo = $asdfgInfo & "~machineid='" & $MachineID & "'" & @CRLF dim $OrgGroups $OrgGroups= StringSplit($MachineID,".") dim $OrgGroupIndex $OrgGroupIndex = UBound($OrgGroups) - 1 $asdfgInfo = $asdfgInfo & "~organization='" & $OrgGroups[$OrgGroupIndex] & "'" & @CRLF dim $EmailUser $EmailUser = GUICtrlRead($txtEmailAddress) $EmailUser = StringMid($EmailUser,1,StringInStr($EmailUser,"@") - 1) $asdfgInfo = $asdfgInfo & "~username='" & $EmailUser & "'" & @CRLF dim $categ $categ = $chkPatrol $asdfgInfo = $asdfgInfo & "~category='" & $categ & "'" & @CRLF dim $track $track = $cChkPatrol $asdfgInfo = $asdfgInfo & "~test='" & $cChkPatrol & "'" & @CRLF $asdfgInfo = $asdfgInfo & "~afterhour='" & $AfterHour & "'" & @CRLF endif Return $asdfgInfo endFunc  
      Yes it was same app as previous post in the forum I asked, I also having experience this happening before but I fail to understand the reason why this is happening. Thanks in advance.
      ButtonSupport - Copy.au3
    • Fhelipe
      By Fhelipe
      So I wanted to use my machine as a server for a program that had a unique login system. It can only log in 1 person and the password is in the script itself. I would like to adptar my code or change it completely so that I can create a database and the Script At Time Of Login Get Database Login And Log In
      Experience with Autoit: Basic                      (sorry for bad English)
      Experience with SQL: None                              (translator only)
      Code Currently Used To Login:
      ;=========================================================
      While 1
          $MSG = GUIGetMsg()
          Switch $MSG
          Case $ButtonOk
              If VerifyLogin(GUICtrlRead($USERNAME),GUICtrlRead($PASSWORD)) = 1 Then
                  GUIDelete($Form1)
                  MsgBox(-1,"Logado com Sucesso","Login Sucedido, Para Parar o Aimbot Pressione ESC!")
                  Run ("C:\Program Files (x86)\Steam\steamapps\common\Mitos.is The Game\Mitosis.exe")
                  RunP()
              Else
                  MsgBox(-1,"Error","Usúario ou Senha está incorreto, Ou Ainda Não Pagou!")
               EndIf
          Case -3
              Exit
           Case $ButtonCancel
              Exit
                Case $HelpButton
               MsgBox(-1, "Criadores", "Criadores: Zummey & Ralta")
      Case $Register
         MsgBox(-1, "ERRO", "PROGRAMA EM DESENVOLVIMENTO FUNÇÃO DESABILITADA")
          EndSwitch
      WEnd
      Func VerifyLogin($USERNAME,$PASSWORD)
          If $USERNAME = "Zummey" And $PASSWORD = "xd90fe10" Then
              Return 1
          Else
              Return 0
          EndIf
      EndFunc; End login
      ;=========================================================