### Bookstore

 Computer Math and Games in Pascal (preview) Lazarus Handbook

### Author Topic: Why the difference in pixels per inch and actual distance on screen ?  (Read 3210 times)

#### newman

• Jr. Member
• Posts: 75
##### Why the difference in pixels per inch and actual distance on screen ?
« on: August 04, 2016, 07:02:18 am »
Hi,

I am trying to draw a line on a Form using Canvas.Line. I find that the pixels per inch is shown as 96, so a line 96 pixels should be exactly 1 Inch long ?

However I find that the line is short, but increasing length to 101 pixels makes it almost 1 inch long. I measure the line on the screen using a scale.

Could anyone explain to me why this difference is there ?

I would like to draw line of exact lengths on the screen, is there any scaling factor that is involved ?

Thanks
a

• Hero Member
• Posts: 10516
##### Re: Why the difference in pixels per inch and actual distance on screen ?
« Reply #1 on: August 04, 2016, 07:20:13 am »
Under Windows you can use the transformation API to translate pixels into real world space and the other way around, with accurate measurements.
This API is specifically designed for accurate drawing.
I don't know if there is something similar for other platforms, but I suppose there is.

Here is some code I copied from a KOL demo I wrote.
It *should* work under Lazarus but I am unable to test it right now, due to a bet that I could use RPi3 as my default computer (all's well, thusfar..). It should get you going anyway. It's is almost pure windows api stuff.
Code: Javascript  [Select][+][-]
1. uses Windows, Forms;
2. const
3.   SCALE         = 5;
4.   TRANSLATE     = 4;
5.   ROTATE        = 3;
6.   SHEAR         = 2;
7.   REFLECT       = 1;
8.   NORMAL        = 0;
9.
10. {\$IFDEF API} // If defined draws with pure api functions (smaller code)
11. procedure TransformAndDraw(Transform:TXform;DC:HDC);
12. var
13.     //TransForm:TXform;
14.     Rect:TRect;
15. begin
16.     // Set the mapping mode to LOENGLISH. This moves the
17.     // client area origin from the upper left corner of the
18.     // window to the lower left corner (this also reorients
19.     // the y-axis so that drawing operations occur in a true
20.     // Cartesian space). It guarantees portability so that
21.     // the object drawn retains its dimensions on any display.
23.     SetMapMode(DC, MM_LOENGLISH);  //inches, for metric select LOMETRIC
24.     // Set the appropriate world transformation (based on the
26.     case iTransform of
27.         SCALE:        // Scale to 1/2 of the original size.
28.         begin
29.             TransForm.eM11 :=  0.5;
30.             TransForm.eM12 :=  0.0;
31.             TransForm.eM21 :=  0.0;
32.             TransForm.eM22 :=  0.5;
33.             TransForm.eDx  :=  0.0;
34.             TransForm.eDy  :=  0.0;
35.             SetWorldTransform(DC, TransForm);
36.         end;
37.         TRANSLATE:   // Translate right by 3/4 inch.
38.         begin
39.             TransForm.eM11 :=  1.0;
40.             TransForm.eM12 :=  0.0;
41.             TransForm.eM21 :=  0.0;
42.             TransForm.eM22 :=  1.0;
43.             TransForm.eDx  :=  75.0;
44.             TransForm.eDy  :=  0.0;
45.             SetWorldTransform(DC, TransForm);
46.         end;
47.         ROTATE:      // Rotate 30 degrees counterclockwise.
48.         begin
49.             TransForm.eM11 :=  0.8660;
50.             TransForm.eM12 :=  -0.5000;
51.             TransForm.eM21 :=  0.5000;
52.             TransForm.eM22 :=  0.8660;
53.             TransForm.eDx  :=  0.0;
54.             TransForm.eDy  :=  0.0;
55.             SetWorldTransform(DC, TransForm);
56.         end;
57.         SHEAR:       // Shear along the x-axis with a
58.         begin                  // proportionality constant of 1.0.
59.             TransForm.eM11 :=  1.0;
60.             TransForm.eM12 :=  1.0;
61.             TransForm.eM21 :=  0.0;
62.             TransForm.eM22 :=  1.0;
63.             TransForm.eDx  :=  0.0;
64.             TransForm.eDy  :=  0.0;
65.             SetWorldTransform(DC, TransForm);
66.         end;
67.         REFLECT:     // Reflect about a horizontal axis.
68.         begin
69.             TransForm.eM11 :=  1.0;
70.             TransForm.eM12 :=  0.0;
71.             TransForm.eM21 :=  0.0;
72.             TransForm.eM22 :=  -1.0;
73.             TransForm.eDx  :=  0.0;
74.             TransForm.eDy  :=  0.0;
75.             SetWorldTransform(DC, TransForm);
76.         end;
77.         NORMAL:      // Set the unity transformation.
78.         begin
79.             TransForm.eM11 :=  1.0;
80.             TransForm.eM12 :=  0.0;
81.             TransForm.eM21 :=  0.0;
82.             TransForm.eM22 :=  1.0;
83.             TransForm.eDx  :=  0.0;
84.             TransForm.eDy  :=  0.0;
85.             SetWorldTransform(DC, TransForm);
86.         end;
87.     end;
88.
89.     // Find the midpoint of the client area.
90.     GetClientRect(Form1.form.Handle, rect);
91.     DPtoLP(DC, rect, 2);
92.     // Select a hollow brush.
93.     SelectObject(DC, GetStockObject(HOLLOW_BRUSH));
94.     // Draw the exterior circle.
95.     Ellipse(DC, Round(rect.right / 2 - 100), Round(rect.bottom / 2 + 100),
96.         Round(rect.right / 2 + 100), Round(rect.bottom / 2 - 100));
97.     // Draw the interior circle.
98.     Ellipse(DC, Round(rect.right / 2 -94), Round(rect.bottom / 2 + 94),
99.         Round(rect.right / 2 + 94), Round(rect.bottom / 2 - 94));
100.     // Draw the key.
101.     Rectangle(DC, Round(rect.right / 2 - 13), Round(rect.bottom / 2 + 113),
102.         Round(rect.right / 2 + 13), Round(rect.bottom / 2 + 50));
103.     Rectangle(DC, Round(rect.right / 2 - 13), Round(rect.bottom / 2 + 96),
104.         Round(rect.right / 2 + 13), Round(rect.bottom / 2 + 50));
105.
106.     // Draw a diagonal line
107.     MoveToEx(DC, Round(rect.right/2 ), Round(rect.bottom / 2 + 100), 0);
108.     LineTo(DC, Round(rect.right / 2 +100), Round(rect.bottom / 2 + 0));
109.
110.     // Draw the horizontal lines.
111.     MoveToEx(DC, Round(rect.right/2 - 150), Round(rect.bottom / 2 + 0), 0);
112.     LineTo(DC, Round(rect.right / 2 - 16), Round(rect.bottom / 2 + 0));
113.     MoveToEx(DC, Round(rect.right / 2 - 13), Round(rect.bottom / 2 + 0), 0);
114.     LineTo(DC, Round(rect.right / 2 + 13), Round(rect.bottom / 2 + 0));
115.     MoveToEx(DC, Round(rect.right / 2 + 16), Round(rect.bottom / 2 + 0), 0);
116.     LineTo(DC, Round(rect.right / 2 + 150), Round(rect.bottom / 2 + 0));
117.
118.     // Draw the vertical lines.
119.     MoveToEx(DC, Round(rect.right/2 + 0), Round(rect.bottom / 2 - 150), 0);
120.     LineTo(DC, Round(rect.right / 2 + 0), Round(rect.bottom / 2 - 16));
121.     MoveToEx(DC, Round(rect.right / 2 + 0), Round(rect.bottom / 2 - 13), 0);
122.     LineTo(DC, Round(rect.right / 2 + 0), Round(rect.bottom / 2 + 13));
123.     MoveToEx(DC, Round(rect.right / 2 + 0), Round(rect.bottom / 2 + 16), 0);
124.     LineTo(DC, Round(rect.right / 2 + 0), Round(rect.bottom / 2 + 150));
125. end;
126.

Note that this draws in true cartesian space, coordinates start at bottom left, not top left. For  Top left or you can change the LOENGLISH into HIENGLISH resp. LOMETRIC into HIMETRIC, I believe.
This preserves space on whatever monitor is connected. Always the correct size.

Note this is translated from the C example sourcecode in Delphi's win32.hlp so you might find the original in C on MSDN.
[EDIT]
The example is indeed on MSDN https://msdn.microsoft.com/en-us/library/windows/desktop/dd145174%28v=vs.85%29.aspx

You can use the inner body of this code ad verbatim in the TForm.OnPaint event and use the form canvas dc.
This code as-is doesn't work yet, because it depends on a form canvas. See code. Factor it out and define a Rect....

Note that the normal drawing functions from Lazarus should also work, provided you do the housekeeping for the canvas (SetGraphicsMode, SetMapMode, SetWorldTransform) You do not have to use the WINAPI drawing functions...

If it needs to be XPlatform, maybe somebody else can add to this example.

« Last Edit: August 04, 2016, 09:41:43 am by Thaddy »

#### newman

• Jr. Member
• Posts: 75
##### Re: Why the difference in pixels per inch and actual distance on screen ?
« Reply #2 on: August 04, 2016, 10:08:38 am »
Sorry I forgot to mention, I am running Lazarus on Ubuntu 16.04

• Hero Member
• Posts: 10516
##### Re: Why the difference in pixels per inch and actual distance on screen ?
« Reply #3 on: August 04, 2016, 10:33:18 am »
Well, this should be possible under Ubuntu too. Lot's of CAD programs use linux so there must be a comparable API.

#### howardpc

• Hero Member
• Posts: 3551
##### Re: Why the difference in pixels per inch and actual distance on screen ?
« Reply #4 on: August 04, 2016, 11:07:36 am »
The Qt widgetset, for instance, has similar functionality to the Windows API Thaddy mentioned ( QPainter::setWorldTransform(), QPainter::worldTransform() ). You could use these on Linux if you can find (or write) a Pascal binding for the Qt headers.

However, I think the correspondence between the actual distance between  pixel centres and the software-mapped distance between pixel centres may vary slightly according to the monitor hardware and drivers in use on your system. Monitors, after all, are not manufactured as measuring devices, but as (reasonably accurate) display devices.