Why does my text look different in GDI+ and in GDI?

http://windowsclient.net/articles/gdiptext.aspx

GDI+ Text, Resolution Independence, and Rendering Methods.

Or – Why does my text look different in GDI+ and in GDI?

  1. Summary
  2. Resolution independent layout
  3. Grid fitting and hinting, and their disproportionate effect on glyph widths
  4. How GDI+ compensates for Grid Fitting.
  5. How to display adjacent text

Summary

GDI+ text layout is resolution independent, and thus different from GDI.

Forms built with GDI+ text look the same at all resolutions and when printed.

In grid-fitted rendering (the default), font hinting usually changes the width of glyphs. When a sequence of glyphs all increase significantly in width GDI+ may have to close up the text to remain resolution independent. In pathological cases (such as a long run of bold lower case ‘l’s in 8 pt Microsoft Sans Serif on a 96 dpi display), the space between some letters can disappear completely.

Resolution independent layout

The GDI+ APIs DrawString and MeasureString lay out text independent of device resolution, thus a paragraph of text takes the same number of lines, no matter what device it is displayed on. There are many benefits:

  • If a form field is sized to fit some static text on one developers machine, it will fit that text on all machines the application runs on, regardless of screen resolution, or accessibility settings.
  • When the form is printed, it will layout the same as it looks on the screen.
  • A form recorded in a metafile retains its layout.

Consider the forms designer working with resolution dependant layout. A box is defined on the form for some text – a title maybe. The title is typed, and the box adjusted to fit. Sadly, unlike the lines, the text will not scale linearly with resolution, so the box will only fit correctly at the resolution the designer was working in.

For graphical objects, such as lines and pictures, a different device resolution simply means a different scale factor at display time. A line 100 pixels long on a 96 dot per inch (dpi) display, will be drawn 125 pixels long on a 120 dpi display, and 625 pixels long on a 600 dpi printer.

For text, the font height will be scaled appropriately for the device resolution: a font that is 20 pixels high on a 96 dpi screen will be rendered 25 pixels high on a 125 dpi screen and 125 pixels high on a 600dpi printer. However the width of individual glyphs will scale only approximately with the height. The exact width is also dependant on hinting (or grid fitting) that has been included in the font to adjust glyphs for legibility.

Grid fitting and hinting, and their disproportionate effect on glyph widths

Grid Fitting, also known as hinting, is the process of adjusting the position of pixels in a rendered glyph to make the glyph easily legible at smaller sizes. Techniques include aligning glyph stems on whole pixels and ensuring similar features of a glyph are affected equally. Font designers spend many hours per glyph defining hinting.

For example, consider the letters ‘elsw’ from Times New Roman, rendered at 8 points on various resolutions, using GDIs standard grid fitting.

In this chart, each glyph is drawn at high resolution in gray, then the actual pixels representing it at a given size and dpi are drawn on top as black circles. 96 dpi is the most widely used display resolution, also known as ‘small fonts’ in control panel/display/settings/advanced/general. 120 dpi corresponds to ‘large fonts’. 150dpi is becoming a common laptop LCD screen resolution. 600dpi is a current low end laser printers resolution.

Notice how at 96 dpi (standard screen resolution) there are very few pixels in an 8pt glyph. The 8pt 96dpi lower-case ‘s’ for example has almost none of the character of the glyph it is intended to portray.

The figures below each glyph show the difference between the designed width of the glyph, and the width after grid fitting.  The left hand figure is the difference as a positive or negative percent. The right hand figure is the difference in pixels at the display resolution. For example, the top left glyph, the 8pt 96dpi lower-case ‘e’ is about 11% or .61 pixels narrower after grid fitting.

If there were no grid fitting, we would expect the only difference between designed and displayed width would be the effect of rounding to the nearest pixel. In this case we would never see a width difference of more than 1/2 a pixel.  At the highest resolution (2400dpi) this is indeed the case, the biggest difference seen here amounting to 0.37 pixels.

However at lower resolutions the effect of grid fitting can exceed or swamp simple rounding.

How GDI+ compensates for Grid Fitting.

When grid fitting generates glyphs narrower than designed

The worst case above is the 96dpi lower-case ‘w’. ‘w’ is a particularly difficult glyph to hint well: the stems must appear symmetrical, evenly spaced and equally thick. Careful hinting has resulted in a good appearance, but the hinted glyph is over 2 pixels narrower than its design width. A string composed only of 8pt 96dpi ‘w’s will be 23% shorter when grid fitted.

When GDI+ displays a line of grid fitted glyphs that are shorter than their design width, it follows these general rules:

  1. The line is allowed to contract by up to an em without any change of glyph spacing.
  2. Remaining contraction is made up by increasing the width of any spaces between words, to a maximum of doubling.
  3. Remaining contraction is made up by introducing blanks pixels between glyphs.

The following example shows how GDI and GDI+ display the string ‘wwwww wwwww wwwww wwwww wwwww’ in 8pt Times New Roman at 96dpi with grid fitting.

GDI (resolution dependant) display
GDI+ (resolution independent) display

This shows

  1. GDI+ is using design widths to layout the string, and so measures he whole string longer than GDI.
  2. GDI+ has allowed the string to stop short of the far end by 1 em
  3. GDI+ has placed remaining expansion in the spaces.

The following example takes the same strings with the spaces removed. Now GDI+ cannot use the spaces to compensate for the contractions caused by grid fitting and instead inserts an extra pixel between some of the glyphs.

GDI (resolution dependant) display
GDI+ (resolution independent) display

When grid fitting generates glyphs wider than designed

Now consider the following chart of Microsoft Sans Serif Bold 8pt. Microsoft Sans Serif is the default user interface font for Windows 2000 and higher.

In this case most of the glyphs are wider than design at 96 and 120dpi. Although many are not much larger, there are some particularly difficult cases.

Consider a string of 96dpi lower-case ‘l’s. Although each ‘l’ is only .16 pixels wider than its design width, a run of just 7 ‘l’s is enough to exceed the runs design width by a whole pixel. In this case we need to compress the string by one pixel. Unfortunately the shape of the lower case ‘l’ behaves very poorly when a pair are overlapped by one pixel: since there is only one blank pixel column, overlapping causes the adjacent glyphs to become solid.

The following example shows a run of 19 ‘l’s displayed by GDI and by GDI+

GDI (resolution dependant) display
GDI+ (resolution independent) display

Notice how in GDI+ the last two ‘l’s are touching.

You can also see in this example that GDI+ adds a small amount (1/6 em) to each end of every string displayed. This 1/6 em allows for glyphs with overhanging ends (such as italic ‘f‘), and also gives GDI+ a small amount of leeway to help with grid fitting expansion.

How to display adjacent text

Maybe you would like to display two strings side by side such that they appear as one string. You might do this if you are writing an editor, or are displaying text with a formatting change inside the paragraph.

Warning: Building lines of text with multiple DrawString calls is inherently unable to display general International text. In particular, In Arabic, Hebrew, Farsi and other right-to-left languages, strings advance generally from right to left, with localized order reversal around numbers and around western phrases. DrawString handles within one output, using bidirectional behaviour defined by Unicode. The rules are complex. See The Unicode Standard Version 3.0 section 3.12.

The default action of DrawString will work against you in displaying adjacent runs: Firstly the default StringFormat adds an extra 1/6 em at each end of each output; Secondly, when grid fitted widths are less than designed, the string is allowed to contract by up to an em.

To avoid these problems:

  1. Always pass MeasureString and DrawString a StringFormat based on the typographic StringFormat (GenericTypographic).
  2. Set the Graphics TextRenderingHint to TextRenderingHintAntiAlias. This rendering method uses anti-aliasing and sub-pixel glyph positioning to avoid the need for grid-fitting, and is thus inherently resolution independent.

The following table compares GDI, GDI+ GridFitted and GDI+ anti-alias text for the examples considered above.

GDI (resolution dependant) display
GDI+ (resolution independent) grid fitted display
GDI+ (resolution independent) anti alias display

While Anti-alias text can look a little gray at very small sizes (this is 8pt), it shows the shape of the glyphs far more accurately than grid fitted text, and does not suffer from the glyph position adjustment described above for grid fitting.


多重解析度與應用視覺影像切割法

按一下以存取 skdoc_2_10.pdf


about DC discussion

View Full Version : What’s the difference between “Handle" and “Device Context"?

Gilbert_the_man
01-15-2004, 05:40 PM
Hi greetings, I have no problem drawing my own forms but I am trying to use my application to draw something on a JAVA applet that pops out from IE.I am reading loads of graphic samples, now I am confused about “Handle" and “Device Context (hDC)". Do they mean the same thing? Or does Handle.hDC = Device Context????

Thank you for your help!
Gilbert

passel
01-15-2004, 09:07 PM
I’m not sure about Handle.hDC, but my understanding is that a Handle is
essentially used to Identify a window, whereas the Device Context
describes the characteristics of the current drawing environment of the
window (among other uses). So if you know the handle to a window, you can use that to
get the Device Context for that window, but they are not the same
thing.
For instance, the Handle to Desktop will always be 0, so you can call
GetDC(0), to retrieve a Device Context for this window, but I assume the
DC will never be 0.Don’t know if you want more detail than that.

rpgnewbie
01-15-2004, 09:45 PM
I am reading loads of graphic samples, now I am confused about “Handle" and “Device Context (hDC)". Do they mean the same thing? Or does Handle.hDC = Device Context????Thank you for your help!
Gilbert

Passel is essentially right if a little vague. The best way to think of a Device Context is a memory space reserved to hold graphics. There are Device Contexts that hold the whole computer screen, for the printer, and VB forms, as well as many VB controls, have Device Contexts attached to them to hold graphics (pictures). The controls that do not have device contexts are called “lightweight" controls and use the device context of their “container", which is usually a form, but could also be a frame or picbox. There are also things called MemoryDCs, which are device contexts that do not appear on the computer screen.

The Handle to a Device Context, or hDC, is essentially a number that points to the memory space defined by the device context. It is generated on the fly at runtime, so should never be assumed or stored as a variable.

Because using device contexts “captures" a chunk of memory they should be used sparingly (so the systems doesn’t run out of memory) and always “released" when not in use. In regard to an IE window you will probably need to know the hwnd (or handle to that window) before you can get the device context of that window to draw on.

passel
01-15-2004, 11:23 PM
Yes, I was brief, because I wasn’t sure getting into the details of Device
Contexts was necessary. The key points being, they are not the same.
The handle identifies a window, and the device context holds the drawing
context of a device, whether it be screen, printer, plotter, etc…And as you partly pointed out, there are different forms of Device Context,
which I thought would not be necessary to start getting into, because
it can be a long disertation.

For instance some windows belong to a class with the CS_OWNDC class
style. These windows have their own Device Context (not shared with
other windows) and the device context is always available. The
Windows System does not modify the device context in any way. The
disadvantage, of course, is that the memory is taken out of the windows
resource space. Visual Basic forms and picture controls have the
CS_OWNDC style.
Since these DCs are permanent you don’t have to release them.
If I do a GetDC(picture2.hWnd), I can do my drawing, and what not and
never release it.

There are two permanent DCs associated with a picturebox. The .hdc
property will equal one, or the other depending on the state of .AutoRedraw.

If AutoRedraw is false, then the .hDC is set to the Device Context that
is associated with area of the screen corresponding with the client area
of the picturebox.

If AutoRedraw is true, then the .hDC is set to the Device Context that is
associated with the memory Device Context (the one associate with the
.image property of the picture box)

When you do a GetDC(Picture2.hWnd), the DC returned is the one
associated with the Screen (the same DC in the .hDC when AutoRedraw
is False) regardless of whether AutoRedraw is set True or not.

So, with AutoRedraw set, you could use the .hDC property to write
information to the memory context, then do a .refresh and see the
results in the picturebox. You could then use GetDC to get the other
Device Context and draw other information into the windows that you
just want to be there temporarily. When you want to “erase" the
temporary information, a simple .refresh will “remove" the drawing to the
“screen" Device Context.

Enough of that.

Another “type" of Device Context is the one you get when you do
something like GetDC (0). This gives you a Device Context that is
associated with the Desktop, but this is not a Device Context that is
permanently associated with the Desktop, like the ones for a Form or Picturebox.
The Device Context returned is know as a Cached Device context. To
conserve resourses, Windows maintains a chache of internal device
contexts that are shared by all applications, and may be used with any
window.
With the Picturebox, if we do a GetDC, we alway get the same DC
everytime because it’s permanently associated with the picturebox.
With the desktop, we will get a different DC each time we call GetDC. The
DC is comming from the cache of shared DC’s. Since there is a limited
number of these, we should keep them only as long as we need them,
releasing them as soon as posible using ReleaseDC. They must never be
destroyed (you DON’T want to run out of these).

A third type of DC is the one you create yourself using CreateDC, or
CreateCompatibleDC. This DC belongs to you, so you can hang on to it
as long as you want. Since it is a newly created DC, there is a fair amount
of overhead associated with them, so you should hang on to them as long
as you need them. Whenever you have no more use for it, or when you
exit your program, you should destroy these DCs.

Well, that’s the basics. Whether you wanted to know it or not.

rpgnewbie
01-16-2004, 01:47 AM
…which I thought would not be necessary to start getting into, because
it can be a long disertation.For instance some windows belong to a class with the CS_OWNDC class
style. These windows have their own Device Context (not shared with
other windows) and the device context is always available.

Disertation, Yes. But I just can’t get enough of this stuff! Thanks 🙂

P.S. CS_OWNDC is news to me (but I don’t get out much…)

Gilbert_the_man
01-16-2004, 03:48 AM
Hi guys thank you so much!Though I dont fully understand but now I know what to do.
I use lHandle = FindWindow(..) to get the handle of my target JAVA window. Then I use lTargetDC = GetDC(iHandle) to get the Device Context. Then I use LineTo(lTargetDC, x, y, Point) to draw my stuff on the target JAVA window!

I was not able to draw for I used LineTo(lHandle…) to draw, now I know I should use LineTo(lTargetDC…) to draw! Bravo!

Thank you guys!
Gilbert

Gilbert_the_man
01-16-2004, 03:55 AM
Now I have another problem.
I am not drawing on my own form, I am drawing on a JAVA applete that pops out from surfing the net.I cant get my “AutoRedraw" and “ScaleMode" right!!!????

After I get the handle (as lHandle) and device context (as lTargetDC) of my target JAVA window, I do the following and they are all wrong!??

lTargetDC.AutoRedraw = True <— compile error
lTargetDC.ScaleMode = vbPixels <— compile error

lHandle.AutoRedraw = True <— compile error
lHandle.ScaleMode = vbPixels <— compile error

How can I set the AutoRedraw and ScaleMode!!??????

Gilbert_the_man
01-16-2004, 05:24 AM
Oh no I still cant draw on my target JAVA applet window….I just test the handle/DC and draw on the WinZip window and it works. But when I test it on a JAVA applet window that pops out from IE then it doesnt work!!??

I dont know where I go wrong!?
Maybe “Layering Graphics" problem??

passel
01-16-2004, 10:03 AM
AutoRedraw and ScaleMode are properties of the PictureBox control and
has nothing to Do with a Device Context.You have no idea what DC is returned by the Java window.
It may a memory DC and you will be drawing in memory but you won’t
see your drawing until the window is refeshed by the Java applet.
And the Java applet may not refresh the windows until it has drawn
something on the window itself (which could involve a complete redrawing
which would wipe out the drawing you did).

Personally, I think drawing in someone elses DC is a bit unorthodox, and
likely not to be worth it since you don’t have real control over everything
else that happens to that DC.

Gilbert_the_man
01-17-2004, 11:36 PM
Thank you for your info, passel.Now I find the handle of my taget java window and get it’s positon, then i draw on the DC of the whole screen covering on that java window since I know its postion. Not the best way tho, but i still done it.