Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandr Miloslavskiy2020-02-26 07:02:35 -0500
committerAlexandr Miloslavskiy2020-02-26 08:45:10 -0500
commitea3abff6bd9beebe3e808492b4a9c7622b1bfe5e (patch)
tree051f8b0e2cac9b69f6d08b67fcc387a52d1f8f7d /tests/org.eclipse.swt.tests.win32
parenteb7aad4583497be87cad2520e40e1f346d9c1ba9 (diff)
downloadeclipse.platform.swt-ea3abff6bd9beebe3e808492b4a9c7622b1bfe5e.tar.gz
eclipse.platform.swt-ea3abff6bd9beebe3e808492b4a9c7622b1bfe5e.tar.xz
eclipse.platform.swt-ea3abff6bd9beebe3e808492b4a9c7622b1bfe5e.zip
Bug 560546 - Differences between GC.drawText() and GC.drawString()
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 Change-Id: I0758a90fef9bc06f350d18d0f3e6fe79a43fb450 Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Diffstat (limited to 'tests/org.eclipse.swt.tests.win32')
-rw-r--r--tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug560546_GC_drawString_vs_GC_drawText.java158
1 files changed, 158 insertions, 0 deletions
diff --git a/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug560546_GC_drawString_vs_GC_drawText.java b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug560546_GC_drawString_vs_GC_drawText.java
new file mode 100644
index 0000000000..4929496f61
--- /dev/null
+++ b/tests/org.eclipse.swt.tests.win32/ManualTests/org/eclipse/swt/tests/win32/snippets/Bug560546_GC_drawString_vs_GC_drawText.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (c) 2020 Syntevo and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Syntevo - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.tests.win32.snippets;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.widgets.*;
+
+public class Bug560546_GC_drawString_vs_GC_drawText {
+ static Font fontSegoeUI;
+ static Font fontSegoeUISymbol;
+
+ static class TestString
+ {
+ TestString(String caption, String string) {
+ this.caption = caption;
+ this.string = string;
+ }
+
+ String caption;
+ String string;
+ }
+
+ static TestString testStrings[] = {
+ new TestString("1-char string", "A"),
+ new TestString("10-char string", "ABCDEFGHIJ"),
+ new TestString("100-char string", "ABCDEFGHIJ012345678'ABCDEFGHIJ012345678'ABCDEFGHIJ012345678'ABCDEFGHIJ012345678'ABCDEFGHIJ012345678'"),
+ };
+
+ static abstract class TestFunction
+ {
+ String caption;
+ abstract void function(GC gc, int x, int y, String string);
+ }
+
+ static class TestDrawString extends TestFunction {
+ TestDrawString() {
+ caption = "GC.drawString";
+ }
+
+ @Override
+ void function(GC gc, int x, int y, String string) {
+ gc.drawString(string, x, y);
+ }
+ }
+
+ static class TestDrawText extends TestFunction {
+ TestDrawText() {
+ caption = "GC.drawText";
+ }
+
+ @Override
+ void function(GC gc, int x, int y, String string) {
+ gc.drawText(string, x, y);
+ }
+ }
+
+ static TestFunction testFunctions[] = {
+ new TestDrawText(),
+ new TestDrawString(),
+ };
+
+ static void testSpeeds(Shell shell) {
+ final int finalIterations = 20 * 1000;
+ String report = "";
+
+ Image image = new Image(shell.getDisplay(), 2000, 100);
+ GC gc = new GC(image);
+ for (int isFinalCalc = 0; isFinalCalc < 2; isFinalCalc++) {
+ for (int iTestString = 0; iTestString < testStrings.length; iTestString++) {
+ for (int iTestFunction = 0; iTestFunction < testFunctions.length; iTestFunction++) {
+ TestString testString = testStrings[iTestString];
+ TestFunction testFunction = testFunctions[iTestFunction];
+
+ // Warm up before measuring
+ final int iterations = (isFinalCalc != 0) ? finalIterations : 10;
+
+ final long time1 = System.nanoTime();
+ for (int iIteration = 0; iIteration < iterations; iIteration++) {
+ testFunction.function(gc, 0, 0, testString.string);
+ }
+ final long time2 = System.nanoTime();
+
+ if (isFinalCalc != 0) {
+ final double elapsed = (time2 - time1) / 1000000000.0;
+ report += String.format("%s, %s - %.3f sec\n", testString.caption, testFunction.caption, elapsed);
+ }
+ }
+ }
+ }
+
+ gc.dispose();
+ image.dispose();
+
+ MessageBox messageBox = new MessageBox(shell);
+ messageBox.setMessage(report);
+ messageBox.open();
+ }
+
+ public static void main(String[] args) {
+ final Display display = new Display();
+ final Shell shell = new Shell(display);
+
+ Font systemFont = display.getSystemFont();
+ int systemFontHeight = systemFont.getFontData()[0].getHeight();
+ fontSegoeUI = new Font(display, "Segoe UI", systemFontHeight, 0);
+ fontSegoeUISymbol = new Font(display, "Segoe UI Symbol", systemFontHeight, 0);
+
+ final Button btnTestSpeed = new Button(shell, SWT.PUSH);
+ btnTestSpeed.setText("Test speeds");
+ btnTestSpeed.addListener(SWT.Selection, event -> testSpeeds(shell));
+ btnTestSpeed.setBounds(10, 10, 100, 20);
+
+ final Canvas canvas = new Canvas(shell, SWT.DOUBLE_BUFFERED | SWT.NONE);
+ canvas.addListener(SWT.Paint, event -> {
+ final GC gc = event.gc;
+
+ int x = 10;
+ int y = 10;
+
+ gc.setFont(fontSegoeUI);
+ gc.drawText("\u26b7 Segoe UI, GC.drawText", x, y);
+ y += 20;
+
+ gc.setFont(fontSegoeUISymbol);
+ gc.drawText("\u26b7 Segoe UI Symbol, GC.drawText", x, y);
+ y += 20;
+
+ gc.setFont(fontSegoeUI);
+ gc.drawString("\u26b7 Segoe UI, GC.drawString", x, y);
+ y += 20;
+
+ gc.setFont(fontSegoeUISymbol);
+ gc.drawString("\u26b7 Segoe UI, GC.drawString", x, y);
+ });
+ canvas.setBounds(10, 40, 300, 200);
+
+ shell.setSize(400, 200);
+ shell.open();
+
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+ }
+} \ No newline at end of file

Back to the top