Recent

Author Topic: [SOLVED] Screenshot shows only a part of the monitor  (Read 3288 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 768
[SOLVED] Screenshot shows only a part of the monitor
« on: May 21, 2024, 01:18:47 pm »
I use the following code, which shall take a screenshot of the whole monitor:
Code: Pascal  [Select][+][-]
  1. uses Graphics, LCLType, LCLIntf;
  2.  
  3. procedure make_screenshot_PNG(filespec: string);
  4.    {creates a screenshot and saves it into a PNG-file}
  5.    var PNG: TPortableNetworkGraphic;
  6.        ScreenDC: HDC; {= THandle}
  7.    begin
  8.    ScreenDC:=GetDC(0);
  9.    PNG:=TPortableNetworkGraphic.Create;
  10.    try
  11.       PNG.LoadFromDevice(ScreenDC);
  12.       PNG.SaveToFile(filespec);
  13.    finally
  14.       PNG.Free;
  15.       ReleaseDC(0,ScreenDC);
  16.    end; {try}
  17.    end;

On most computers this works correctly, but one of my users has the following problem: the screenshots show not the complete monitor, both on the right side and the bottom side a part is missing. See attached screenshots: "screen03_bad.png" = 1536 x 864 pixel is what my program created, while "screen04_ok.jpg" = 1920 x 1080 pixel is a correct screenshot taken by another tool.

The OS of this user is Windows 10 Pro, 64-bit.
I compiled with Lazarus 2.0.10 / FPC 3.2.0 / 32-bit.

I'm not familiar with this DPI stuff, but I run this code on the computer of this user
Code: Pascal  [Select][+][-]
  1. procedure show_DPI_factors;
  2.    {shows some DPI-factors}
  3.    var x,y,m: longint;
  4.    begin
  5.    x:=-1; y:=-1;
  6.    if Graphics.ScreenInfo.Initialized then
  7.       begin
  8.       x:=Graphics.ScreenInfo.PixelsPerInchX;
  9.       y:=Graphics.ScreenInfo.PixelsPerInchY;
  10.       end;
  11.  
  12.    write(Forms.Screen.PixelsPerInch, ' ', x, ' ', y, ' ',
  13.          Form1.DesignTimePPI, ' ', Form1.PixelsPerInch);
  14.  
  15.    for m:=0 to Screen.MonitorCount-1 do
  16.       write(' ', Forms.Screen.Monitors[m].PixelsPerInch);
  17.    writeln;
  18.    end;

and the result was: 96 96 96 96 96 96 (which looks normal to me).

Question 1: are there others who see the same problem?
Question 2: can anybody help?
Thanks in advance.
« Last Edit: May 30, 2024, 11:54:42 am by Hartmut »

Josh

  • Hero Member
  • *****
  • Posts: 1310
Re: Screenshot shows only a part of the monitor
« Reply #1 on: May 21, 2024, 01:35:08 pm »
Just a thought.

Do you have High DPI on for your project.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Hartmut

  • Hero Member
  • *****
  • Posts: 768
Re: Screenshot shows only a part of the monitor
« Reply #2 on: May 21, 2024, 02:47:17 pm »
Thanks Josh for that thought. "LCL scaling (Hi-DPI)" and "DPI awareness" are both off. I experimented with them 2 years ago in this project and run into problems (but I don't remember more details - too long ago), so I want to keep them off.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Screenshot shows only a part of the monitor
« Reply #3 on: May 21, 2024, 08:30:13 pm »
Here is how I do, try if it works on the complicated system:
Code: Pascal  [Select][+][-]
  1. function TForm1.CreateScreenShot(const AMonitor: Integer; const AExcludeForm: Boolean = True): TBitmap;
  2. var
  3.   LMon: TMonitor;
  4.   ScreenCanvas: TCanvas;
  5. begin
  6.   if ((AMonitor < 0) or (AMonitor >= Screen.MonitorCount)) then
  7.     Exit;
  8.   LMon := Screen.Monitors[AMonitor];
  9.   if AExcludeForm then
  10.     begin
  11.       Self.Hide;
  12.       Application.ProcessMessages;
  13.       Sleep(250);
  14.     end;
  15.  
  16.   ScreenCanvas := TCanvas.Create;
  17.   try
  18.     ScreenCanvas.Handle := GetDC(0);
  19.     Result := TBitmap.Create;
  20.     try
  21.       Result.PixelFormat := pf32bit;
  22.       Result.SetSize(LMon.WorkareaRect.Width, LMon.WorkareaRect.Height);
  23.       Result.Canvas.CopyMode := cmSrcCopy;
  24.       Result.Canvas.CopyRect(Result.Canvas.ClipRect, ScreenCanvas, LMon.BoundsRect);
  25.     finally
  26.     end;
  27.   finally
  28.     ScreenCanvas.Free;
  29.   end;
  30.   if AExcludeForm then
  31.     Self.Show;
  32. end;
Attached is a demo project with monitor selector.
If that is not working as expected, its worth a try to change the DC like: "ScreenCanvas.Handle := GetDC(LMon.MonitorNum);"

//edit:
Please update source by removing all "WorkareaRect" calls, exemplary:
"Result.SetSize(LMon.WorkareaRect.Width, LMon.WorkareaRect.Height);" --> "Result.SetSize(LMon.Width, LMon.Height);"
« Last Edit: May 21, 2024, 09:31:17 pm by KodeZwerg »
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Screenshot shows only a part of the monitor
« Reply #4 on: May 21, 2024, 09:51:54 pm »
Small feature added to choose if you want to snapshot also the taskbar.
Code: Pascal  [Select][+][-]
  1. function TForm1.CreateScreenShot(const AMonitor: Integer; const AExcludeForm: Boolean = True; const AExcludeTaskbar: Boolean = False): TBitmap;
  2. var
  3.   LMon: TMonitor;
  4.   LScreen: TCanvas;
  5.   LRect: TRect;
  6. begin
  7.   if ((AMonitor < 0) or (AMonitor >= Screen.MonitorCount)) then
  8.     Exit;
  9.  
  10.   if AExcludeForm then
  11.     begin
  12.       Self.Hide;
  13.       Application.ProcessMessages;
  14.       Sleep(250);
  15.     end;
  16.  
  17.   LMon := Screen.Monitors[AMonitor];
  18.  
  19.   if AExcludeTaskbar then
  20.     LRect := LMon.WorkareaRect
  21.   else
  22.     LRect := LMon.BoundsRect;
  23.  
  24.   LScreen := TCanvas.Create;
  25.   try
  26.     LScreen.Handle := GetDC(LMon.MonitorNum);
  27.     Result := TBitmap.Create;
  28.     try
  29.       Result.PixelFormat := pf32bit;
  30.       Result.SetSize(LRect.Width, LRect.Height);
  31.       Result.Canvas.CopyMode := cmSrcCopy;
  32.       Result.Canvas.CopyRect(Result.Canvas.ClipRect, LScreen, LRect);
  33.     finally
  34.       ReleaseDC(LMon.MonitorNum, LScreen.Handle);
  35.     end;
  36.   finally
  37.     LScreen.Free;
  38.   end;
  39.   if AExcludeForm then
  40.     Self.Show;
  41. end;
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Hartmut

  • Hero Member
  • *****
  • Posts: 768
Re: Screenshot shows only a part of the monitor
« Reply #5 on: May 22, 2024, 12:50:11 pm »
Thank you KodeZwerg for your code. I tried your attachment from reply #3 on my computer both on Linux Ubuntu 22.04 and Windows 7 and both failed (with my code from my 1st post this works perfectly).

Here is what I changed in your attachment:
 - removed "WorkareaRect" 4 times as you had requested
 - added "ReleaseDC(0, ScreenCanvas.Handle);" in line 82 in function "CreateScreenShot" because I think this was missing
 - added "bmp.SaveToFile('d:\Tst\xxx.bmp');" in line 106 in procedure "Button1Click" to save the bitmap to a file.

The result is, that your screenshots show only a part of the monitor: both on the right side and the bottom side a part is missing. See attached screenshots: "xx282_bad.zip" is what your program created, while "screen_0282_ok.png" is a correct screenshot taken by another tool.

That means, now I have (with your code) nearly the same problem on my computer as the problematic user has (with my code from my 1st post, which works perfectly on my computer both with Linux and Windows). The only difference is:
 - the screenshot on the problematic computer (with my code) is only 1536 x 864 pixel, while this computer has a resulution of 1920 x 1080 pixel (so the screenshot is too small, has not enough pixels)
 - the screenshot on my computer (with your code) has the correct size (1280 x 1024 pixel), but the picture is augmented, so a part is missing on the right side and bottom side.

Very strange. Has somebody an explanation for that? Or an idea for a solution?

wp

  • Hero Member
  • *****
  • Posts: 12069
Re: Screenshot shows only a part of the monitor
« Reply #6 on: May 22, 2024, 01:31:56 pm »
Tested KodeZwerg's project from reply #3 without any modifications, and it works perfectly:
- Win 11 / Laz main/FPC 3.2.2 (32 bit), 96ppi
- Win 11 / Laz main/FPC 3.2.2 (64 bit), 96 ppi
- Win 7 / Laz main/FPC3.2.2 (32 bit), 120 ppi
- Win 7 / Laz 2.0.10/FPC 3.20 (32 bit), 120 ppi
« Last Edit: May 22, 2024, 02:00:59 pm by wp »

TRon

  • Hero Member
  • *****
  • Posts: 2790
Re: Screenshot shows only a part of the monitor
« Reply #7 on: May 22, 2024, 01:43:19 pm »
Does not seem to work as intended on Linux Mate (gtk2). It actually does not seem to be doing anything at all  %)

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Screenshot shows only a part of the monitor
« Reply #8 on: May 22, 2024, 01:52:13 pm »
Thank you KodeZwerg for your code. I tried your attachment from reply #3 on my computer both on Linux Ubuntu 22.04 and Windows 7 and both failed (with my code from my 1st post this works perfectly).
You are welcomed, the only difference I see on your screen is that you are using GTK2 widgetset where I am more than unaware if it support the stuff I use.
I've tested on Windows 10 64bit with win64 widgetset and run into no problems on whatever resolution/dpi.

Lazarus 3.99 (rev main_3_99-1993-g5ad1922ccb) FPC 3.2.2 x86_64-win64-win32/win64
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Screenshot shows only a part of the monitor
« Reply #9 on: May 22, 2024, 04:03:38 pm »
Does not seem to work as intended on Linux Mate (gtk2). It actually does not seem to be doing anything at all  %)
How do you access screen/desktop canvas (similar to GetDC()) on Linux Mate (gtk2) ?
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

Hartmut

  • Hero Member
  • *****
  • Posts: 768
Re: Screenshot shows only a part of the monitor
« Reply #10 on: May 22, 2024, 05:47:42 pm »
Tested KodeZwerg's project from reply #3 without any modifications, and it works perfectly:
- Win 11 / Laz main/FPC 3.2.2 (32 bit), 96ppi
- Win 11 / Laz main/FPC 3.2.2 (64 bit), 96 ppi
- Win 7 / Laz main/FPC3.2.2 (32 bit), 120 ppi
- Win 7 / Laz 2.0.10/FPC 3.20 (32 bit), 120 ppi

Thank you very much wp for those tests. Because you tested without modifications, I added again "WorkareaRect" 4 times and repeated the tests on my computer on both Linux Ubuntu and Windows 7. But I got the same results as before: the screenshots show only a part of the monitor, both on the right side and the bottom side a part is missing. Only the heights is now some pixel less than before. Sadly the "WorkareaRect" was not the root cause.



Does not seem to work as intended on Linux Mate (gtk2). It actually does not seem to be doing anything at all  %)

Thanks TRon. On my Linux I first had the same problem. Therefore I added "bmp.SaveToFile('xxx.bmp');" in procedure "Button1Click" to save the bitmap into a file:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   bmp: TBitmap;
  4. begin
  5.   if ((cbMonitors.ItemIndex < 0) or (cbMonitors.ItemIndex >= cbMonitors.Items.Count)) then
  6.     Exit;
  7.   bmp := CreateScreenShot(cbMonitors.ItemIndex, cbExclude.Checked);
  8.   try
  9.     bmp.SaveToFile('xxx.bmp');
  10.     Image1.Picture.Assign(bmp);
  11.   finally
  12.     bmp.Free;
  13.   end;
  14. end;

Would you test with this again? And what Lazarus/FPC version are you using?



You are welcomed, the only difference I see on your screen is that you are using GTK2 widgetset where I am more than unaware if it support the stuff I use.
I've tested on Windows 10 64bit with win64 widgetset and run into no problems on whatever resolution/dpi.

Lazarus 3.99 (rev main_3_99-1993-g5ad1922ccb) FPC 3.2.2 x86_64-win64-win32/win64

Thanks for this informations. As said, on Windows 7 I have the same problem with your code as on Linux GTK2.

TRon

  • Hero Member
  • *****
  • Posts: 2790
Re: Screenshot shows only a part of the monitor
« Reply #11 on: May 23, 2024, 09:19:48 am »
Sorry for the delay.

Does not seem to work as intended on Linux Mate (gtk2). It actually does not seem to be doing anything at all  %)
How do you access screen/desktop canvas (similar to GetDC()) on Linux Mate (gtk2) ?
That is the stupid ... usually exactly the same.

Thanks TRon. On my Linux I first had the same problem. Therefore I added "bmp.SaveToFile('xxx.bmp');" in procedure "Button1Click" to save the bitmap into a file:
Yes, the saved picture indeed contains the caption of the desktop

Quote
Would you test with this again? And what Lazarus/FPC version are you using?
See above answer and it doesn't matter which Lazarus/FPC version, I've tried with Lazarus 3.0, 3.2 and trunk with FPC 3.2.2.

I'll love to have a closer look to figure out what's going on there but am a bit busy atm so that might take some time.

KodeZwerg

  • Hero Member
  • *****
  • Posts: 2269
  • Fifty shades of code.
    • Delphi & FreePascal
Re: Screenshot shows only a part of the monitor
« Reply #12 on: May 23, 2024, 10:46:05 am »
I added again "WorkareaRect" 4 times and repeated the tests on my computer on both Linux Ubuntu and Windows 7. But I got the same results as before: the screenshots show only a part of the monitor, both on the right side and the bottom side a part is missing. Only the heights is now some pixel less than before. Sadly the "WorkareaRect" was not the root cause.
You might switch to the last version, there in code you see what the WorkareaRect is for, it is similar to a forms ClientRect where the titlebar and frame + eventually shadow is excluded, the area where you can paint in, for the desktop you got the same, WorkareaRect is the part of the screen where each app is displayed on = excluding the taskbar.
In the demo project I simply added another checkbox to set that argument (Exclude Taskbar).
Anyway, it would not change a thing since the work with canvas is somehow blocked by OS, unsure if its in general the try to access the screen canvas on gtk2.
Will patiently wait for TRon :D
That is the stupid ... usually exactly the same.

I'll love to have a closer look to figure out what's going on there but am a bit busy atm so that might take some time.
Thank you for help finding a solution for gtk2!
« Last Edit: Tomorrow at 31:76:97 xm by KodeZwerg »

TRon

  • Hero Member
  • *****
  • Posts: 2790
Re: Screenshot shows only a part of the monitor
« Reply #13 on: May 23, 2024, 10:57:04 am »
That is the stupid ... usually exactly the same.
I'll love to have a closer look to figure out what's going on there but am a bit busy atm so that might take some time.
Thank you for help finding a solution for gtk2!
I can at least provide the hint to change the pixel depth to 24 bit. That provides a visible picture. Now to figure out how to test for that  :)

Hartmut

  • Hero Member
  • *****
  • Posts: 768
Re: Screenshot shows only a part of the monitor
« Reply #14 on: May 23, 2024, 01:37:50 pm »
Thanks TRon. On my Linux I first had the same problem. Therefore I added "bmp.SaveToFile('xxx.bmp');" in procedure "Button1Click" to save the bitmap into a file:
Yes, the saved picture indeed contains the caption of the desktop

Thanks TRon for this test. Do I understand you correctly, that this screenshot shows the *complete* desktop? Nothing missing on the right margin or bottom margin?



You might switch to the last version, there in code you see what the WorkareaRect is for, it is similar to a forms ClientRect where the titlebar and frame + eventually shadow is excluded, the area where you can paint in, for the desktop you got the same, WorkareaRect is the part of the screen where each app is displayed on = excluding the taskbar.
In the demo project I simply added another checkbox to set that argument (Exclude Taskbar).
Anyway, it would not change a thing since the work with canvas is somehow blocked by OS, unsure if its in general the try to access the screen canvas on gtk2.

Thanks for this explanations.



I can at least provide the hint to change the pixel depth to 24 bit. That provides a visible picture. Now to figure out how to test for that  :)

I can confirm this. On Linux Ubuntu with 'pf32bit' the Image 'Form1.Image1' stays empty (nothing shown), while with 'pf24bit' a small screenshot is displayed there.

 

TinyPortal © 2005-2018