Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Win32] ImageDataProvider: incorrectly reported zoom level #1579

Open
tmssngr opened this issue Nov 8, 2024 · 6 comments
Open

[Win32] ImageDataProvider: incorrectly reported zoom level #1579

tmssngr opened this issue Nov 8, 2024 · 6 comments

Comments

@tmssngr
Copy link
Contributor

tmssngr commented Nov 8, 2024

Describe the bug
An ImageDataProvider can be used to provide different ImageData instances for the passed zoom level. This does not work correctly - the passed zoom level is incorrect. It "snaps" to multiples of 100. For 150% zoom level on Windows 11, I'm getting the value 100, for 175% zoom level I'm getting 200.

To Reproduce
Run this snippet on Windows 11 with different monitor zoom levels (I only have one 4k monitor attached):

import java.io.*;
import java.nio.file.*;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.layout.*;
import org.eclipse.swt.widgets.*;

public class ImageDpiTest {

	public static void main(String[] args) throws IOException {
		final ImageData imageData1x;
		try (InputStream inputStream = Files.newInputStream(Paths.get("test1x.png"))) {
			imageData1x = new ImageData(inputStream);
		}

		final ImageData imageData2x;
		try (InputStream inputStream = Files.newInputStream(Paths.get("test2x.png"))) {
			imageData2x = new ImageData(inputStream);
		}

		final Display display = new Display();
		final Image image = new Image(display, (ImageDataProvider)zoom -> {
			System.out.println("zoom = " + zoom);
			return zoom >= 150 ? imageData2x : imageData1x;
		});

		final Shell shell = new Shell(display);
		shell.setLayout(new GridLayout(1, false));

		final Label label = new Label(shell, SWT.NORMAL);
		label.setImage(image);
		label.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));

		shell.setSize(400, 300);
		shell.open();

		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}

		display.dispose();
	}
}

It will log the reported zoom level.

Expected behavior
For 150% Windows/monitor zoom level, I expect 150 to be reported, for 175% I expect to get 175. Otherwise the image datas I report back are too small for 150% zoom level.

Screenshots
For 150% zoom level:
zoom-150% - 100
For 175% zoom level:
zoom-175% - 200

Environment:

  1. Select the platform(s) on which the behavior is seen:
    • All OS
    • Windows
    • Linux
    • macOS
  1. Additional OS info (e.g. OS version, Linux Desktop, etc)
    Tried on Windows 11 23H2 with one 32" 4k monitor attached.

Workaround (or) Additional context
Set swt.autoScale to false, but this will cause other problems.

@HeikoKlare
Copy link
Contributor

Expected behavior
For 150% Windows/monitor zoom level, I expect 150 to be reported, for 175% I expect to get 175. Otherwise the image datas I report back are too small for 150% zoom level.

Can you please explain where this expectation comes from? The behavior should have never been like this. Actually, the behavior of having the device zoom being "rounded" to multiples of 100 is intended as being established several years ago: https://eclipse.dev/eclipse/news/4.6/platform.php#swt-autoscale-tweaks

If the application is supposed to scale everything according to the actual native zoom (including images), you should use swt.autoScale=quarter or swt.autoScale=exact.

@tmssngr
Copy link
Contributor Author

tmssngr commented Nov 8, 2024

My expectation comes from the fact that numbers like 100 or 200 are used. If the rounding to 100 would be the goal, I would have expected the use of 1 and 2 (factor).

@tmssngr
Copy link
Contributor Author

tmssngr commented Nov 8, 2024

Background: until now we were using swt.autoScale=none because this gave us access to the full resolution on Windows with 4k monitors.

Is there also a mode where my solely my ImageDataProvider decides what image to use (without any magic auto-zooming under the hood)?

BTW, how I can request the actual shell's zoom level now?

@HeikoKlare
Copy link
Contributor

HeikoKlare commented Nov 8, 2024

I have to admit that I do not understand the actual issue: is there any behavior that has changed? With swt.autoScale=none, the zoom value should have always been rounded to a multiple of 100. none is not a valid value for swt.autoScale, so the code always defaults back to this:

if (zoom == 0) { // || "integer".equalsIgnoreCase (value) || "integer200".equalsIgnoreCase (value)
zoom = Math.max ((nativeDeviceZoom + 25) / 100 * 100, 100);
}

Also the image constructor retrieving the image data has always taken this device zoom, if I am not mistaken. So would be interesting to know where the behavior has actually changed in comparison to, e.g., the previous release of Eclipse SWT.

Is there also a mode where my solely my ImageDataProvider decides what image to use (without any magic auto-zooming under the hood)?

In the ImageDataProvider, you can do whatever you want. No one forces you to consider the zoom value passed to it. However, the question is how you want to provide properly scaled image data if you do not consider the context and its scaling. With the improved HiDPI support coming up (https://eclipse.dev/eclipse/news/4.34/platform.php#rescale-on-runtime-preference), you need to consider the context in which an image is used to adapt it to the zoom level of the monitor currently working on. If you do not want to support this kind of functionality in the future, you could fall back to, e.g., the non-API DPIUtil#getNativeDeviceZoom() to retrieve the original zoom value of the OS.

BTW, how I can request the actual shell's zoom level now?

A shell does not have any public API to retrieve its zoom. It has the getZoom() method to retrieve the zoom used by SWT (which in your scenario is 100/200/...) and it has nativeZoom with the original native zoom of the application, but none of them is accessible outside SWT. In which scenario do you need to retrieve the zoom of a shell? See comment below

@laeubi
Copy link
Contributor

laeubi commented Nov 8, 2024

A shell does not have any public API to retrieve its zoom.

Shell.getMonitor().getZoom() ?

@tmssngr
Copy link
Contributor Author

tmssngr commented Nov 8, 2024

In the ImageDataProvider, you can do whatever you want. No one forces you to consider the zoom value passed to it. However, the question is how you want to provide properly scaled image data if you do not consider the context and its scaling.

I've tried our application with the new SWT library with swt.autoScale set to quarter or exact. It caused auto-scaling for some images (e.g. in buttons), because there were some scaling artefacts visible, e.g. duplicate pixels where in the 100% and 200% images were just 1-pixel width.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants