Bug 560546 - Differences between GC.drawText() and GC.drawString()
Summary: Differences between GC.drawText() and GC.drawString()
Status: RESOLVED FIXED
Alias: None
Product: Platform
Classification: Eclipse Project
Component: SWT (show other bugs)
Version: 4.15   Edit
Hardware: PC Windows 10
: P3 normal (vote)
Target Milestone: 4.15 RC2   Edit
Assignee: Alexandr Miloslavskiy CLA
QA Contact: Niraj Modi CLA
URL:
Whiteboard:
Keywords: Documentation
Depends on:
Blocks:
 
Reported: 2020-02-26 06:58 EST by Alexandr Miloslavskiy CLA
Modified: 2020-03-02 05:22 EST (History)
4 users (show)

See Also:
niraj.modi: documentation+
niraj.modi: review+


Attachments
Screenshot of test snippet on Win10 (12.97 KB, image/png)
2020-02-26 08:54 EST, Alexandr Miloslavskiy CLA
no flags Details
Native snippet (gives very similar results) (15.94 KB, text/plain)
2020-02-26 09:03 EST, Alexandr Miloslavskiy CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alexandr Miloslavskiy CLA 2020-02-26 06:58:05 EST

    
Comment 1 Eclipse Genie CLA 2020-02-26 08:47:34 EST
New Gerrit change created: https://git.eclipse.org/r/158404
Comment 2 Alexandr Miloslavskiy CLA 2020-02-26 08:48:43 EST
This patch documents subtle differences between these two APIs on Windows.

For reference, a copy of commit message:
----------------------------------------
The rest of this commit message explains behavior on Windows.

`GC.drawString()` uses WINAPI `ExtTextOut()`.
`GC.drawText()` uses WINAPI `DrawText()`.

`DrawText()` does some additional font fallback resolving. When the
currently selected font doesn't have a glyph, it will use a hardcoded
table of fallback fonts, which seem to give good enough results.

The pseudocode for table lookup is:
  int iCandidate = gdi32full!iStandardFallbackCandidates[SCRIPT_ITEM.a.eScript];
  int iFallback  = gdi32full!iStandardFallback[iCandidate];
  gdi32full!StandardFallbackFont[iFallback];

`DrawText()` will then call `ExtTextOut()` with different fonts and
related string parts.

`ExtTextOut()` is a kernel-mode API. Its user-mode counterpart merely
queues a job for kernel. As a consequence, it's faster then `DrawText()`
which does additional processing. The kernel worker of `ExtTextOut()` is
`win32kfull.sys!GreBatchTextOut`.

`ExtTextOut()` does not try to find fallback fonts, but will process
font links defined in this registry key:
  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontLink\SystemLink

However, this doesn't work as good: font link settings are different on
different Windows versions, and it also seems to do a poor job at
matching font size.

The attached snippet demonstrates the differences:
* On Win8.1, 'Segoe UI' is not linked to 'Segoe UI Symbol', and
  `GC.drawString()` will show a "missing glyph" (rectangle glyph).
* On Win10, 'Segoe UI' is linked, but the glyph's size is wrong.

Performance as measured on my Win10:
1-char string,   DrawText   - 0.741 sec
1-char string,   ExtTextOut - 0.101 sec
10-char string,  DrawText   - 0.962 sec
10-char string,  ExtTextOut - 0.225 sec
100-char string, DrawText   - 2.893 sec
100-char string, ExtTextOut - 1.533 sec
Comment 3 Alexandr Miloslavskiy CLA 2020-02-26 08:54:14 EST
Created attachment 281937 [details]
Screenshot of test snippet on Win10

Notice that 'SegoeUI, drawString' produces a smaller glyph
Comment 4 Alexandr Miloslavskiy CLA 2020-02-26 09:03:17 EST
Created attachment 281938 [details]
Native snippet (gives very similar results)
Comment 5 Nikita Nemkin CLA 2020-02-28 11:24:09 EST
This is rather minor, compared to differences with GDI+ text rendering.
Comment 7 Niraj Modi CLA 2020-03-02 05:19:27 EST
(In reply to Eclipse Genie from comment #1)
> New Gerrit change created: https://git.eclipse.org/r/158404

+1 for JavaDoc only changes for RC2
Comment 8 Niraj Modi CLA 2020-03-02 05:22:25 EST
(In reply to Eclipse Genie from comment #6)
> Gerrit change https://git.eclipse.org/r/158404 was merged to [master].
> Commit:
> http://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/
> ?id=ea3abff6bd9beebe3e808492b4a9c7622b1bfe5e

Thanks Alexandr for detailing the API difference, resolving now.