Recent

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

newman

  • Jr. Member
  • **
  • Posts: 75
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

Thaddy

  • Hero Member
  • *****
  • Posts: 14387
  • Sensorship about opinions does not belong here.
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.
Google on SetWorldTransform.
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.
  22.     SetGraphicsMode(DC, GM_ADVANCED);
  23.     SetMapMode(DC, MM_LOENGLISH);  //inches, for metric select LOMETRIC
  24.     // Set the appropriate world transformation (based on the
  25.     // user's menu selection).
  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 »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

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

thanks anyway for the reply.

Thaddy

  • Hero Member
  • *****
  • Posts: 14387
  • Sensorship about opinions does not belong here.
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.
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
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.

Thaddy

  • Hero Member
  • *****
  • Posts: 14387
  • Sensorship about opinions does not belong here.
Re: Why the difference in pixels per inch and actual distance on screen ?
« Reply #5 on: August 04, 2016, 02:59:28 pm »
That is correct. You also need decent driver software.
e.g. The distance is always within +1 or -1 pixel width on proper monitors, but a full HD screen of 24'' is less precise than a full HD screen of 18'' and soforth.
That is because the smaller monitor uses more pixels for the same actual real life distance with less of a rounding error.
You have to realize that!
Nowadays most monitors can be queried for display size and resolution, even cheap ones.
« Last Edit: August 04, 2016, 03:02:29 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

 

TinyPortal © 2005-2018