Scaling of coordinates between different screen resolutions - delphi

I have projects developed in Delphi 10 on a laptop with a screen resolution of 96. I am now using Delphi 10.4 Community Edition on a Microsoft Surface with a screen resolution of 201. Is there a function or a settings that automatically converts numerically defined coordinates when scaling an application? To show what I mean I add this code snippet.
procedure TForm1.Button1Click(Sender: TObject);
begin
with Canvas do
begin
MoveTo(0,0);
LineTo(400,250);
MoveTo(0,0);
LineTo(ClientWidth,ClientHeight);
end;
end;
The first line drawn does not scale, the second one obviously does.
May I add:
If I compile the same code on my old laptop with a screen resolution of 96 and then run the exe file on my Surface Laptop with a screen resolution of 201 it scales ok, I was hoping there was a facility somewhere to compile my old programmes on my new computer without having to manually change all the code referring to coordinates x and y.

There's no built-in scaling of coordinates in a TCanvas. You can use this CLASS HELPER:
TYPE
TFormHelper = CLASS HELPER FOR TForm
FUNCTION Scale(Value : INTEGER) : INTEGER;
END;
FUNCTION TFormHelper.Scale(Value : INTEGER) : INTEGER;
BEGIN
Result:=MulDiv(Value,CurrentPPI,Screen.DefaultPixelsPerInch)
END;
Use it as in:
procedure TForm1.Button1Click(Sender: TObject);
begin
with Canvas do
begin
MoveTo(Scale(0),Scale(0)); // Not needed, as 0 scaled is always 0, but... //
LineTo(Scale(400),Scale(250));
MoveTo(0,0);
LineTo(ClientWidth,ClientHeight);
end;
end;

To run my projects developed with Delphi 10 on my new computer with a higher screen resolution, using Delphi 10.4 Community Edition I change the setting
Project Options -> Application -> Manifest -> DPI Awareness to GDI Scaling and it all works like a charm.

Related

Taking a screenshot of a game work if is in dx9 but dont if is dx11

If i run a game in dx11 i get a white box with PrintWindow and a black box with GetDC/BitBlt but in dx9 mode it work correctly...
I tried to find another way with google but i cant find one working also i read that both don't work with hardware accellerated windows, but isnt dx9 hardware accellerated ?
PS.
If i do a screenshot of the whole desktop i get the dx11 window content.
PPS.
I need the specific window only, cropping the desktop wont do because in dx9 i can get the content of the whole window even if half window is outside the monitor while with the desktop i get it cut.
EDIT:
Windows is 10 21H2
The game is "Guild Wars 2", it can run in both dx9 and dx11.
This is the code working in dx9 mode even with the window outside the monitor:
function TakeSS(WindowHandle: HWND; FullWindow: boolean = true): TBitMap;
var
vRect: TRect;
vFlag: Cardinal;
GibRect: function(h: THandle; r: TRect): longbool;
begin
if FullWindow then
begin
GibRect := #GetWindowRect;
vFlag := PW_RENDERFULLCONTENT;
end
else
begin
GibRect := #GetClientRect;
vFlag := PW_CLIENTONLY;
end;
if GibRect(WindowHandle, vRect) then
begin
result := TBitMap.Create(vRect.Width, vRect.Height);
PrintWindow(WindowHandle, result.Canvas.Handle, vFlag);
end
else
result := nil;
end;

Delphi Webcam Snapshot [duplicate]

This question already has answers here:
How to get a snapshot from a webcam with Delphi7 using VFrames(TVideoImage)
(2 answers)
Closed 8 years ago.
I need to find a foolproof way with Delphi XE to take a snapshot using a webcam and save it as a picture file. There are many posts about this but none of them work for me. Either the code won't compile or the image is not created. The best one so far is this one using VFrames. It shows the web cam video perfectly but when I try to save a snapshot to a bmp using the code below the image is blank white. I tried assigning it to a TImage with the same result. Does anyone know why this happens or have a better way to do this?
procedure TForm1.Button2Click(Sender: TObject);
var
cam:TVideoImage;
strlst:TStringList;
BMP:TBitmap;
begin
strlst := TStringList.Create ;
cam :=TVideoImage.Create;
cam.GetListOfDevices(strlst);
cam.VideoStart(strlst.Strings[0]); //specify a cam by number
//get snapshot
BMP := TBitmap.Create;
cam.GetBitmap(BMP);
BMP.SaveToFile('C:\test.bmp');
cam.VideoStop;
BMP.Free;
end;
I Think you should have a look a DirectX and I think you should have a look at this site :
http://www.delphibasics.info/home/delphibasicsprojects/directxdelphiwebcamcaptureexample
This page offers a great way to use webcams under windows, its very reliable and allows you to set any cam property. Its all opensource, easy to use and based on native windows DirectX libraries.
Use this sample:
http://www.delphibasics.info/home/delphibasicsprojects/directxdelphiwebcamcaptureexample/DirectXDelphiWebcamCapture.rar
This is a directX conversion to pascal made by michael#grizzlymotion.com
add VSample.pas and VFrames.pas to your project
uses VFrames;
procedure TForm6.Button1Click(Sender: TObject);
var
cam:TVideoImage;
strlst:TStringList;
begin
strlst := TStringList.Create ;
cam := TVideoImage.Create;
cam.GetListOfDevices(strlst);
cam.SetDisplayCanvas(PaintBox1.Canvas);
cam.VideoStart(strlst.Strings[0]) ;//specify your cam by number
end;
Or you can have a look at this : http://sourceforge.net/projects/webcam-cap/ it uses pure API calls to AVICAP32.DLL

How to prevent the screen from automatically rotating on a tablet?

In the link below, Microsoft describes two ways to limit rotation of an application screen on a tablet.
http://msdn.microsoft.com/en-ca/library/windows/apps/hh700342.aspx
what's happening is that delphi's (XE3) TRibbon doesn't handle rotation well. it tends to get hung.
as would be expected, the MS web site describes how to do this from MS development products. I don't see how I can do this in my Delphi project.
Method 1:
add this to your appxmanifest file:
<InitialRotationPreference>
<Rotation Preference="landscape"/>
<Rotation Preference="landscapeFlipped"/>
</InitialRotationPreference>
I haven't yet found where/how the appxmanifest should be part of the application so I can do this in delphi.
Method 2:
call this with code:
Windows.Graphics.Display.DisplayProperties.AutoRotationPreferences =
Windows.Graphics.Display.DisplayOrientations.Landscape;
to migrate this to delphi, I'd need to know API DLL information so I could do something similar.
Any ideas?
Could there be a COM object or DLL that gives us access to this?
Those calls are to disable rotation for a WindowsRT application (FKA Metro) which you cannot build with Delphi (yet). Even a Metropolis app is still a desktop app. There is a solution on the Intel site.
Based on feedback from X-Ray I cleaned up the code:
unit MetroDisplayRotation;
(*
* Usage: TMetroDisplayRotation.SetDisplayAutoRotationPreferences(
* TMetroDisplayRotation.ORIENTATION_PREFERENCE_LANDSCAPE or
* TMetroDisplayRotation.ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED);
*)
interface
type
TMetroDisplayRotation = class
public const
ORIENTATION_PREFERENCE_NONE = $0;
ORIENTATION_PREFERENCE_LANDSCAPE = $1;
ORIENTATION_PREFERENCE_PORTRAIT = $2;
ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED = $4;
ORIENTATION_PREFERENCE_PORTRAIT_FLIPPED = $8;
class procedure SetDisplayAutoRotationPreferences(ORIENTATION_PREFERENCE: Integer);
end;
implementation
uses
SysUtils, Windows;
{ TMetroDisplayRotation }
class procedure TMetroDisplayRotation.SetDisplayAutoRotationPreferences(
ORIENTATION_PREFERENCE: Integer);
type
TSDARP = procedure(ORIENTATION_PREFERENCE: Integer); stdcall;
var
UserHandle: THandle;
SDARP: TSDARP;
begin
UserHandle := GetModuleHandle('User32.dll');
#SDARP := GetProcAddress(UserHandle, 'SetDisplayAutoRotationPreferences');
if Assigned(SDARP) then
SDARP(ORIENTATION_PREFERENCE);
end;
end.
You will want to make sure you ONLY call this on Windows 8 since that procedure doesn't exist elsewhere.
Usage: TMetroDisplayRotation.SetDisplayAutoRotationPreferences(TMetroDisplayRotation.ORIENTATION_PREFERENCE_LANDSCAPE or TMetroDisplayRotation.ORIENTATION_PREFERENCE_LANDSCAPE_FLIPPED);
Another BAD option is to disable it for the entire tablet. Just go to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\AutoRotation in the registry and change Enable to 0.

How to get screen resolution in Firemonkey XE3?

How can I get the screen resolution in Firemonkey 2 (Delphi XE3)?
It's all changed in XE3. The platform support has been completely overhauled. See Pawel Glowacki's article for more.
var
ScreenSvc: IFMXScreenService;
Size: TPointF;
begin
if TPlatformServices.Current.SupportsPlatformService(IFMXScreenService, IInterface(ScreenSvc)) then
begin
Size := ScreenSvc.GetScreenSize;
....
end;
end;
In spite of the overhaul, this is still not much use if you have multiple monitors. Perhaps there is some multimon support in FMX2, but it's clearly not available in IFMXScreenService.

Is TDirect2DCanvas slow or am I doing something wrong?

While looking for alternatives to replace GDI, I was trying to test Delphi's 2010 TDirect2DCanvas performance in Windows 7.
I tested it by drawing a huge polyline using Direct2D and the result was absurdly slow, even with 500 times less data than the amount I've ran the same test using GDI (and I didn't even use a bitmap as backbuffer in GDI, I just drew to the form canvas directly).
So I guess either:
a) Direct2D is slower than GDI;
b) TDirect2DCanvas is slow;
c) I'm doing something wrong
and hopefully it's c).
The test code I wrote is:
unit Unit2;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, Direct2D, D2D1;
type
TForm2 = class(TForm)
private
{ Private declarations }
FD2DCanvas: TDirect2DCanvas;
FData: array[0..50000] of TPoint;
public
procedure CreateWnd; override;
procedure WMSize(var Message: TWMSize); message WM_SIZE;
procedure WMPaint(var Message: TWMPaint); message WM_PAINT;
{ Public declarations }
end;
var
Form2: TForm2;
implementation
uses utils;
{$R *.dfm}
procedure TForm2.CreateWnd;
var
i: Integer;
begin
inherited;
FD2DCanvas := TDirect2DCanvas.Create(Handle);
for i := 0 to High(FData) do begin
FData[i].X := Random(Self.ClientWidth div 2);
FData[i].Y := Random(Self.ClientHeight);
end;
end;
procedure TForm2.WMPaint(var Message: TWMPaint);
var
PaintStruct: TPaintStruct;
begin
BeginPaint(Handle, PaintStruct);
try
FD2DCanvas.BeginDraw;
try
FD2DCanvas.Polyline(FData);
finally
FD2DCanvas.EndDraw;
end;
finally
EndPaint(Handle, PaintStruct);
end;
end;
procedure TForm2.WMSize(var Message: TWMSize);
begin
if Assigned(FD2DCanvas) then begin
ID2D1HwndRenderTarget(FD2DCanvas.RenderTarget).Resize(D2D1SizeU(ClientWidth, ClientHeight));
end;
end;
end.
Also, I'm really willing to draw long polylines in real code, as a system I'm working on need to draw plenty of ~2500 points polylines (at least 10K of them).
Updated (2010-11-06)
I've found out earlier that Direct2D doesn't seem to like polylines, it draws faster if you use a lot of single lines (2 points polylines).
Thanks to Chris Bensen I found out the slowness was with large polylines while using anti-aliasing. So I disabled anti-aliasing as Chris suggested and performance went from ~6000ms to ~3500ms for drawing 50k lines.
Things could still be improved because Direct2D just doesn't handle well polylines while using anti-aliasing. With anti-aliasing disabled it's just the opposite.
Now the time for drawing with Direct2D the 50k lines, if I draw the large polyline without anti-aliasing, is ~50ms. Nice, eh!
The thing is that GDI is still faster than Direct2D if I draw to a bitmap and after it's done I BitBlt the result back to the form, it paints at ~35ms, and with the same graphics quality. And, Direct2D also seems to be using a backbuffer already (it just draws when EndDraw() is called).
So, can this be improved somehow to make using Direct2D worth speed-wise?
Here's the updated code:
type
TArray = array[0..1] of TPoint;
PArray = ^TArray;
procedure TForm2.WMPaint(var Message: TWMPaint);
var
PaintStruct: TPaintStruct;
begin
FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
BeginPaint(Handle, PaintStruct);
try
FD2DCanvas.BeginDraw;
try
FD2DCanvas.Pen.Color := clRed;
FD2DCanvas.Polyline(FData);
finally
FD2DCanvas.EndDraw;
end;
finally
EndPaint(Handle, PaintStruct);
end;
end;
By the way, even if I use Chris' suggestion of creating the geometry beforehand the speed is about the same speed as GDI, but still not faster.
My computer is running Direct3D and OpenGL apps normally and here's dxDiag output: http://mydxdiag.pastebin.com/mfagLWnZ
I'll be glad if anyone can explain me why is this slowness. Sample code is much appreciated.
The problem is antialiasing is turned on. Disable antialiasing and the performance of Direct2D will be on par or faster than GDI. To do that after TDirect2DCanvas is created, make this call:
FD2DCanvas.RenderTarget.SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
TDirect2DCanvas is interface compatible where possible with TCanvas so it can be a drop in replacement with TCanvas, so some of the drawing routines are are a bit inefficient. For example, Polyline creates a geometry each time it is called and throws it away. To increase performance keeping the geometry around.
Take a look at the implementation for TDirect2DCanvas.Polyline and hoist that out into your application for something like this:
procedure TForm2.CreateWnd;
var
i: Integer;
HR: HRESULT;
Sink: ID2D1GeometrySink;
begin
...
D2DFactory.CreatePathGeometry(FGeometry);
HR := FGeometry.Open(Sink);
try
Sink.BeginFigure(D2D1PointF(FData[0].X + 0.5, FData[0].Y + 0.5),
D2D1_FIGURE_BEGIN_HOLLOW);
try
for I := Low(FData) + 1 to High(FData) - 1 do
Sink.AddLine(D2D1PointF(FData[I].X + 0.5, FData[I].Y + 0.5));
finally
Sink.EndFigure(D2D1_FIGURE_END_OPEN);
end;
finally
hr := Sink.Close;
end;
And then draw it like so:
procedure TForm2.WMPaint(var Message: TWMPaint);
begin
FD2DCanvas.BeginDraw;
FD2DCanvas.Pen.Color := clRed;
FD2DCanvas.RenderTarget.DrawGeometry(FGeometry, FD2DCanvas.Pen.Brush.Handle);
FD2DCanvas.EndDraw;
end;
Direct2D relies on the driver and hardware implementation, so you're bound to have performance oddities depending on the hardware and driver you're running on (same bag of issues as 3D rendering engines face).
For instance on the issue of rendering lines, you'll likely face some (hidden) underlying hardware buffer issues: on a given hardware+driver, when drawing a polyline, if the underlying datasize is below a certain threshold, the performance could be high, with full hardware acceleration. Above that threshold, you could be falling back to a partially software or unoptimized path, and performance will plummet. The threshold will depend on hardware, driver and brush/drawing options, can be there, or not.
These are the same issues as when rendering 2D or 3D via OpenGL or regular DirectX, if you stray outside of well trodden rendering paths, things aren't so rosy.
As far as rendering non-antialiased graphics goes, my advice would be to stick with GDI, the implementations are solid with widespread hardware support.
For antialiased graphics, GDI+, Graphics32, AGG, and by and large, software-only solutions are preferable IME whenever you have no control over the end-user hardware. Otherwise, prepare yourself for customer support issues.
In all my benchmark tests OpenGL (with and without MSAA antialiasing) is faster than GDI, GDI+ or Direct2D, for the particular case of drawing 2D elements like polygons, lines, rectangles, etc.
What about GDI+ speed, in comparison?
We wrote a free/open source unit, able to render any VCL TCanvas content (using a TMetaFile) using the GDI+ engine.
In practice, performance is very good, and anti-aliaising was on...
We use this in several projects, drawing regular components content into a bitmap, then using this bitmap for drawing the form content on screen (this will avoid any flicker problem).
And with anti-aliaising, marketing people were happy about the result, and other programmers (using C# or WPF) were wondering how it was working: the drawing is very fast and the applications are reactive (like well built Delphi apps), use very little memory, and the result on screen looks modern (especially if you use Calibri or such fonts if available on your system).
See http://synopse.info/forum/viewtopic.php?id=10
It will work with any version of Delphi (from Delphi 6 up to Delphi XE), and will work on any version of Windows (XP, Vista, Seven - need to deploy the standard gdiplus.dll with previous OS).
Our unit uses pascal code for the GDI to GDI+ conversion on XP, and native Microsoft hidden API under Vista, Seven or if Office 2003/2007 is installed on the PC.

Resources