Introduction
I played an educational game which teaches kids how to use a ruler to draw and
measure a straight line. However, the unit length of the ruler widget on the
computer does not equal to the actual length in the real world. For example, if
you use the ruler widget to draw a 2 cm line, and then use a real ruler to
measure it on the screen, the length is normally much greater than 2 cm.
Although the game is for educational purpose and it is acceptable not to
strictly follow the real length, it would be better if we can provide some
ruler widget that has the same unit length with the real world ruler. Therefore, the
problem we are trying to solve in this article is: Draw a line with length of 1 centimeter on the computer screen,
regardless of the screen’s physical size and configurations, such as
resolutions, orientation, etc.
Solution
The math
part of the solution is pretty simple. Suppose the screen resolution is 1680×1024
and the computer screen’s physical size is 47cm×30cm (a typical 22-inch
display), then horizontally, there are 1680/47 pixels per centimeter.
Therefore, to draw a 1 cm line horizontally, we can just draw a line with 1680/47 pixels. The following is a C# code for the drawing part.
//Draw a horizontal line on the
graphics g with color clr.
//The starting coordinate of the
line defined in x, y.
//The length (in cm) of the
line is defined in length.
//The screen's horizontal
resolution (in pixels) is defined in horizoantalResolution
//The screen's physical width
(in cm) is defined in screenWidth.
private void DrawHorizontalLine(Graphics
g, Color clr, int
x, int y, float
length, int horizoantalResolution, int screenWidth)
{
float pixelpercm = (float)horizoantalResolution / screenWidth;
g.DrawLine(new Pen(new SolidBrush(clr),
1), x, y, x + length * pixelpercm, y);
}
It is easy
to get the screen resolution in C#. The following code can get the resolution
of the primary screen (a computer can connect to multiple screens).
int w = Screen.AllScreens[0].Bounds.Width;
int h = Screen.AllScreens[0].Bounds.Height;
It is a
little bit hard to get the physical size of the computer screen. One technology
we can use is to read the extended display identification data (EDID) of a
connected display. The data structure of EDID can be found in the following Wikipedia
page: http://en.wikipedia.org/wiki/Extended_display_identification_data
. The display physical width and height are defined in the bytes 21 and 22 of EDID.
In Windows,
the EDID are stored in the system registry under [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\DISPLAY\$DISPLAY_NAME\$DISPLAY_ID\Device
Parameters], where $DISPLAY_NAME and
$DISPLAY_ID are display specific
strings. The following C# code example shows how to read the EDID from the registry and extract the physical size of the display from EDID. Note that there may exists more than one EDID entries if the computer has multiple displays connected. Also, we can read other display properties, such as model name, from EDID. Please refer to http://en.wikipedia.org/wiki/Extended_display_identification_data on data format of EDID.
private void
GetDisplays()
{
RegistryKey key = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Enum\\DISPLAY");
if (key == null)
return;
//sName is the display name
foreach (string
sName in key.GetSubKeyNames())
{
{
if
(sName == "Default_Monitor")
continue;
RegistryKey
keyDisplay = key.OpenSubKey(sName);
//sDisplayID is the display ID
foreach (string sDisplayID in
keyDisplay.GetSubKeyNames())
{
RegistryKey
keyDevice = keyDisplay.OpenSubKey(sDisplayID);
if
(keyDevice == null)
continue;
//sDriver is the display driver
name
string
sDriver = (string)keyDevice.GetValue("Driver");
RegistryKey
keyParameter = keyDevice.OpenSubKey("Device
Parameters");
if
(keyParameter == null)
{
keyDevice.Close();
continue;
}
byte[]
EDID = (byte[])keyParameter.GetValue("EDID");
keyParameter.Close();
keyDevice.Close();
if
(EDID == null)
continue;
//Display's physical height and width
int
width = EDID[21];
int
height = EDID[22];
}
keyDisplay.Close();
}
key.Close();
}
Now we have enough information to draw a line, the screen resolution and the physical size of the screen. We can feed these parameters to the DrawHorizontalLine function. Also it is not hard to extend the above horizontal solution to the vertical case or to the line with any slope.
Future Works
- The above solution is for Windows only. For other platforms, such as Mac, Linux, it should be pretty easy to get current screen's resolution. However, we need to do some more research on where EDID are stored in these platforms.
- If the computer has multiple connected displays, we need extra code to determine in which display current application is running and then use the corresponding width and height parameters.
- This solution is also useful for a new "Print Preview" feature, which can produce the preview exactly the same as what will be printed. Imagine the computer shows an A4 paper on the screen with the exact width and height as a real A4 paper, and all the content displayed on the paper has the same size as the content printed on a real A4 paper.
No comments:
Post a Comment