Working with Graphics on Windows in a pixel perfect way​
​by Silvia Hao

Introduction to the problem

The cover GIF is a rasterized version of Graphics with a bunch of usual primitives. They are designed to be exactly 1 pixel width and is intended to align perfectly with your monitor’s pixels at a specific ImageSize (here the first and last frame of the GIF). One frame of the graphics is included below, can you (especially for readers on Windows) find the correct ImageSize value for Show, so the grids align with pixels on your own screen?
Show
,ImageSize->{width,height}
Pixel based screen is one of the most common media when working with vector graphics. Pixel-perfection is essential when doing digital design, UI composition, etc. However, the computer world (or for sake of this post, the modern Windows desktop world) has a lot of units to measure length, some are related to physical units, many aren’t. These units have confused people (including me) for a long time. A recent question on the sister site, Why AbsoluteThickness depends on ImageSize?, motivated me to finally investigate the topic and get the concepts straight for good and for all. Now that I believe I have a correct understanding of the problem, I think it’s really worth sharing my findings as a tech note. Hopefully it can help other confused fellows to find their own ways to work with vector graphics in a pixel perfect way.

Helper functions

ClearAll[pipe,pipeList,branch,branchSeq]​​pipe=RightComposition;​​branch=Through@*{##}&;​​branchSeq=pipe[branch@##,Apply@Sequence]&;​​pipeList=pipe[Fold[branchSeq[Identity,pipe[#2,#1]]&,Identity,Reverse@{##}],List]&;
myRasterize is a modified version of Rasterize, so it works correctly for small raster size:
In[]:=
ClearAll[myRasterize]​​Options[myRasterize]=BackgroundRGBColor
93
255
,
107
255
,
255
255
;​​myRasterize[graph_Graphics,RasterSizew_Integer,OptionsPattern[]]:=​​Block[{System`ConvertersDump`createExportPacketExpr,bg=OptionValue[Background]}​​,System`ConvertersDump`createExportPacketExpr[expr_,opts___]:=System`ConvertersDump`createGraphicExportPacketExpr[expr,opts]//Block[{GraphicsBox=Inactive[GraphicsBox]},#/.(BackgroundNone):>Sequence[]]&//Activate​​;Rasterize[graph,RasterSizew,Backgroundbg]//​​pipe[RemoveAlphaChannel,ImageData[#,"Byte"]&​​,DeleteCases[{ImageData[Image[{{bg}}],"Byte",InterleavingTrue][[1,1]]..}]​​,Image[#,"Byte",ColorSpace"RGB"]&]​​]
Usage:
Graphics[{},BackgroundBlack,ImageSize{10,5}]//pipe[​​branch[​​Rasterize[#,RasterSize10]&​​,myRasterize[#,RasterSize10]&​​]​​,branch[Identity,Map@ImageDimensions]​​,Prepend[{"Rasterize","myRasterize"}]​​,Transpose,Grid]
Out[]=
Rasterize
{10,16}
myRasterize
{10,5}

All the units in the world

The inch is not an inch!

When talking about length on screen, people use inch a lot. Then there are concepts like DPI or dot per inch, PPI or pixel per inch, etc. When I measure my monitor, it’s
34.4cm
width with
1920px
in horizontal. It seems, by “definition” its PPI is
In[]:=
Quantity[1920,"Pixels"]
UnitConvert[Quantity[34.4,"Centimeters"],"Inches"]
Out[]=
141.767
pixels
/in
Right? Now, according to the documentation of ImageSize, Wolfram Language uses print’s point for it and assumes
72pt
per inch. My notebook Magnification is
100
%
, so ImageSize  72 should give me a graphic exactly
1inch
wide (i.e.
2.54cm
), right?
In[]:=
CurrentValue[Magnification]​​Graphics{},BackgroundBlack,AspectRatio
1
10
,ImageSize72
Out[]=
1.
Out[]=
Apparently I’m wrong! It’s around
2.15cm
:
What is happening? I failed to see a connection between
2.54cm
and
2.15cm
. That was the first roadblock on my quest.
After some searching and reading, it turned out (see MSDN reference at the end of the post) that on modern Windows desktop, the inch in DPI/PPI is called logical inch, which has nothing to do with the actual inch, and usually does not equate to an actual inch either.
Firstly, the printer’s point (or pt for short), more officially known as desktop publishing point, is an abstract length unit:
In[]:=
Quantity[1,"PrintersPoints"]//{#,#//QuantityUnit}&
Out[]=

1
pt
,DesktopPublishingPoints
Windows introduced the concept of logical inch (
in
logic
), as I understand it, for compatibility, so this always holds:
72pt1
in
logic
96px
Quote MSDN: “For many years, Windows used the following conversion: One logical inch equals 96 pixels.”

The dot is actually a pixel!

Having understood the logical inch, I can confidently say: Hey! My monitor is working at
96PPI
! But what about DPI? If the inch is to logical inch, the point is a logical point, what is the dot in DPI?
I went out and did some more research. A lot of materials came up, many of them emphasized the difference between DPI and PPI. Unfortunately, that was one of the source of my original confusion, because they are talking about other DPI meanings! Here, for Windows desktop, the dot in DPI IS the same as pixel! Quoting MSDN:
This scaling factor is described as 96 dots per inch (DPI). The term dots derives from printing, where physical dots of ink are put onto paper. For computer displays, it would be more accurate to say 96 pixels per logical inch, but the term DPI has stuck.

But the pixel is not a pixel!

Okay... but wait! If
72pt96px
, why is this Graphics
120px
wide instead of
96px
?
In[]:=
Graphics{},BackgroundBlack,AspectRatio
1
10
,ImageSize72
Out[]=
In[]:=
(*FromWindows'PrintScreen:*)​​
//ImageDimensions
Out[]=
{120,12}
The story only got stranger as it so happens that Windows Direct2D introduced yet another abstract/logical layer. The pixel or dot in relation
1
in
logic
96px
, is something called device-independent pixel or DIP for short. It relates with the actual pixel size (
px
for short) on physical monitors through Windows DPI scaling factor
λ
DPI
, which is
1.25
on my main display:
Simply put, Windows aligns
100DIP
with
125px
on my monitor. Indeed,
96DIP×1.25px/DIP120px
! The same information can also be confirmed from SystemInformation:
In[]:=
SystemInformation["Devices","ConnectedDisplays"]//Lookup["Resolution"]//First
Out[]=
120.
Without diving deeper into the rabbit hole, it’s worth noting that there is still at least one extra layer between DIP and physical pixels: In cases the monitor is not working at native resolution (so the whole desktop and UI could look fuzzy), we’ll have to differentiate rendering pixels (
px
rend
) from physical pixels (
px
phy
). For example:
A monitor with
1920
px
phy
×1080
px
phy
is used, but a non-native resolution
1440
px
rend
×900
px
rend
is set. Then there will be
96
px
phy
×1080
px
phy
area unused both at left and right, and a
1728
px
phy
×1080
px
phy
region at center will be re-mapped to
1440
px
rend
×900
px
rend
for actual display. It means in that case
5
px
rend
6
px
phy
, so physical pixel alignments can happen only at a few certain pixels.
In this post, I shall assume native resolution are used, so
px
phy
≡
px
rend
. Thus without differentiating them,
px
is used afterwards for simplicity.

Wrap-up

In summary, I now have encountered the following units and concepts: (Really a lot for a seemly simple problem!)
in
Ordinaryrealworldinch
in
logic
Logicalinch,noconnectionwithordinaryin
pt
Desktoppublishingpoint
dot
Device-independentpixel,alsoshortformDIPs
DPI
dotsper
in
logic
λ
DPI
WindowsDPIscalingsetting
px
rend
“Renderingpixel”asreferredinOS’screenresolutionsettings
px
phy
Physicalpixelasonphysicaldisplays
They relate to each other by:
72pt1
in
logic
96dot≡96DIPs96
λ
DPI
px
rend
Again, it is good to confirm my findings through SystemInformation:

Drawing Graphics pixel-perfectly

Pixel alignment and anti-aliasing

Success! It looks like a pixel-perfect alignment is achieved (note 244 in first row is due to anti-aliasing):
By varying the horizontal position (i.e. xpos) of the testing line, I was able to identify the linear relationship between pixel coordinate and Graphics coordinate:
Moreover, the test clearly indicates a simple linear anti-aliasing stage. That means pixel value is proportional with fraction of the pixel area covered by graphic primitive.
I did a quick check to make sure that is consistent with what I saw directly from my monitor:
Note Round[255*{1/4, 3/4}] is exactly {64,191}:

Margins -- a hidden pitfall

Careful readers may already noticed my explicit settings on CellMargins and CellFrameMargins in the previous example. It so happens that these two options do affect the final result -- considering that they are both measured in pt, this shouldn’t come out as surprising!
Generally speaking, to place a Graphics, it must be contained in a Cell, which itself is in a Notebook. So parameters determining the Graphics’ position include: the top-left corner of the notebook window, the left and top margins from the Cell to notebook’s boundary, and the left and top margins from the Graphics to Cell’s boundary. They can be specified by WindowMargins, CellMargins and CellFrameMargins respectively. On Windows, a native window’s position is always pixel-aligned, so CellMargins and CellFrameMargins are the only settings I need to worry about. To have a pixel-aligned Graphics, it must first be contained in a cell that has its two margins add up to integral pixels:
A zoom-in of the top-left corner of my screen shows how everything is aligned perfectly:
Further zoom-in of the magenta region:

Neat examples

Case 1

A series of interleaving vertical lines of 1px width:
zoom-in:
They all align with pixels exactly, so no antialias effect is triggered.

Case 2

Some key features from my little artwork:
◼
  • The four corner pixels were left untouched.
  • ◼
  • The left circle touched the rounded rectangle precisely. (Especially comparing with scaled up vector Graphics.)
  • For comparison, this shows a ×6 scale-up of the vector Graphics itself:

    References

    ◼
  • MSDN: DPI and Device-Independent Pixels
  • ◼
  • Wikipedia: Dots per inch