TCanvas.DrawLine behaves differently on Windows and on iOS - delphi

When drawing a list of connected lines using TCanvas.DrawLine they are not drawn connected on iOS, but they are on Windows. Here are the results of a simple test app connecting 40 points:
Targeting iPad 2 on simulator (non-retina, but it also happens on retina) I get this:
Targeting 32-bit Windows I get this:
The code in the test app (to reproduce the problem I have in a much more complex app):
var
LineList: array[0..39] = (
(X:8.00; Y:45.00),
(X:14.00; Y:43.00),
(X:21.00; Y:43.00),
(X:27.00; Y:44.00),
(X:31.00; Y:45.00),
(X:37.00; Y:45.00),
(X:40.00; Y:43.00),
etc.
);
procedure TForm24.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var i: integer;
begin
Canvas.BeginScene;
Canvas.StrokeThickness := 6;
Canvas.Stroke.Kind := TBrushKind.Solid;
Canvas.Stroke.Color := $FF000000;
for i := 1 to 39 do
Canvas.DrawLine(LineList[i-1], LineList[i], 1.0);
Canvas.EndScene;
end;
Note: problem increases the higher value of StrokeThickness. If set to 1 the problem is gone.
I am using Delphi 10 Seattle update 1.
What can I do to solve this?
Edit:
Reported as a bug here:
https://quality.embarcadero.com/browse/RSP-13129

Related

FMX: TCanvas.DrawLine issue on Android

Lines with thickness > 1 appear to be drawn differently on Windows and Android. I'm using Delphi 11.0. Create a blank multi-platform application and add the following in the FormPaint event.
procedure TMainForm.FormPaint(Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
begin
Canvas.Stroke.Thickness := 20;
Canvas.Stroke.Cap := TStrokeCap.Round;
Canvas.Stroke.Color := TAlphaColorRec.Red;
Canvas.Stroke.Kind := TBrushKind.Solid;
Canvas.DrawLine(PointF(20,20), PointF(70,70), 1);
Canvas.DrawLine(PointF(70,70), PointF(130,70), 1);
end;
This results in the following. The same happens when drawing on a TImage.
It seems that in Windows the endpoints of the line are at the center of the line caps, whereas on Android they're at the extremes of the line caps. I'm testing on a Huawei P10 running Android 8.0.0. I'm not currently able to test on a more recent Android version. A Google search doesn't seem to give any results on this issue. I'd appreciate if anyone has any info on this issue and what could be done about it? If someone could test this on a more recent Android version, that would also be appreciated. I could of course add special code for Android to extend the line endpoints by half the line thickness, but I'd like to avoid that if possible.
The Android documentation seem to imply that it shouldn't behave like this.
https://developer.android.com/reference/android/graphics/Paint.Cap
The main difference between Windows and Android is that they use different implementations of TCanvas. Windows uses TCanvasD2D, whereas Android is using TCanvasGpu. Looking into the Delphi code. I wonder if the following code in FMX.StrokeBuilder.pas is causing the issue. This code gets runs from FMX.Canvas.GPU.pas, even with TStrokeDash.Solid. I can't work out why it would offset the ends like that.
procedure TStrokeBuilder.InsertDash(SrcPos, DestPos: TPointF; const DashDirVec, ThickPerp: TPointF);
var
InitIndex, DivIndex, Divisions: Integer;
SinValue, CosValue: Single;
RoundShift: TPointF;
begin
if FBrush.Cap = TStrokeCap.Round then
begin
RoundShift := DashDirVec * FHalfThickness;
SrcPos := SrcPos + RoundShift;
DestPos := DestPos - RoundShift;
end;
I can confirm that TCanvasGpu is the issue by setting FMX.Types.GlobalUseGPUCanvas to True before Application.Initialize. Then TCanvasGpu is used even on Windows instead of TCanvasD2D and I get the same issue that I see on Android.

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;

Why is FindWindow() not 100% reliable?

I'm using this Delphi 7 code to detect if Internet Explorer is running:
function IERunning: Boolean;
begin
Result := FindWindow('IEFrame', NIL) > 0;
end;
This works on 99% of the systems with IE 8,9 and 10.
But there are some systems (unfortunately none of mine, but I have two beta testers which have such systems, both Win7 x64 SP1) where FindWindow() returns 0 for IEFrame, even if IE is in memory.
So I've coded an alternate method to find the window:
function IERunningEx: Boolean;
var WinHandle : HWND;
Name: array[0..255] of Char;
begin
Result := False; // assume no IE window is present
WinHandle := GetTopWindow(GetDesktopWindow);
while WinHandle <> 0 do // go thru the window list
begin
GetClassName(WinHandle, #Name[0], 255);
if (CompareText(string(Name), 'IEFrame') = 0) then
begin // IEFrame found
Result := True;
Exit;
end;
WinHandle := GetNextWindow(WinHandle, GW_HWNDNEXT);
end;
end;
The alternate method works on 100% of all systems.
My question - why is FindWindow() not reliable on some of the systems?
I'm guessing that FindWindow is declared to return a WinHandle, which is a THandle, which is an Integer, which is signed. (At least, I think this was the case many years ago when I programmed in Delphi.)
If IE has a window handle with the top bit set then it will be negative so your test will return False:
Result := FindWindow('IEFrame', NIL) > 0;
Window handles don't usually have the top bit set, but I don't know that it's impossible.
According to Delphi Help, FindWindow(ClassName,WindowName) does not search child windows. This could be the reason for the 1% failures. Maybe in those two beta tester's systems the IEFrame window had WS_CHILD style set.
This would explain why the GetTopWindow/GetNextWindow loop works. GetTopWindow(hWndParent) retrieves the child window at the top of the Z order, and GetNextWindow(hWnd,Direction) retrieves the next child window in the Z order.
This could be tested by calling FindWindowEx(hWndParent,hWndChild,ClassName,WindowName),
to see if it works where FindWindow() fails.

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.

Why do I get "left side cannot be assigned to" for TRect after upgrading Delphi?

I am migrating the code from Delphi 7 to XE2 one of the Graphical module.
we are using TRect variable , the old code is working in Delphi 7 without issue
Ex:
Var
Beold : TRect
begin
Beold.left := Beold.right;
end.
while porting the code to new XE2 we are facing the issue
E0264 : Left side cannot be assigned to
Can you please explain what is the changes in XE2 TRect and D7, how we can assign the valuse
The code you posted compiles and runs fine in a quick Delphi test app, so it's not your real code.
I'd suspect what you've hit is a change in the with statement when it's related to using properties, however. There was a bug in previous versions of Delphi that existed for many years that was finally fixed recently. IIRC, it was first mentioned in a note in the README.HTML file for D2010. It's been added to the documentation in XE2 (not as a behavior change, but the new behavior is documented). The documentation is located here at the docwiki.
(Additional info: It must have been 2010 where it changed; Marco Cantù's Delphi 2010 Handbook mentions it on page 111 as "The With Statement Now Preserves Read-Only Properties" which describes this behavior and the solution I indicated below.)
Instead of accessing the property of a class directly using a with statement, you now need to declare a local variable, and read and write the whole thing directly (error handling omitted for clarity - yes, I know there should be a try..finally block to free the bitmap).
var
R: TRect;
Bmp: TBitmap;
begin
Bmp := TBitmap.Create;
Bmp.Width := 100;
Bmp.Height := 100;
R := Bmp.Canvas.ClipRect;
{ This block will not compile, with the `Left side cannot be assigned to` error
with Bmp.Canvas.ClipRect do
begin
Left := 100;
Right := 100;
end;
}
// The next block compiles fine, because of the local variable being used instead
R := Bmp.Canvas.ClipRect;
with R do
begin
Left := 100;
Right := 100;
end;
Bmp.Canvas.ClipRect := R;
// Do other stuff with bitmap, and free it when you're done.
end.
turns out, using
with (Bmp.Canvas.ClipRect) do
begin
Bottom := 100;
end;
throws error: [Left side cannot be assigned to]
Yet,
with Bmp.Canvas.ClipRect do
begin
Bottom := 100;
end;
does not.
Delphi 10.3.3 is just as finicky about parenthesis as the older versions.

Resources