Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 521887

Summary: [HiDPI][Win32] GC draw line with width 0 is not affected by OS scaling
Product: [Eclipse Project] Platform Reporter: Lothar Lattermann <eklipse>
Component: SWTAssignee: Niraj Modi <niraj.modi>
Status: VERIFIED FIXED QA Contact:
Severity: major    
Priority: P3 CC: info, lshanmug, niraj.modi, peter, simon.scholz
Version: 4.7   
Target Milestone: 4.8 M7   
Hardware: PC   
OS: Windows All   
See Also: https://git.eclipse.org/r/122254
https://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/?id=9a393e8677dae90cf87b17188eb1104b7ca70f93
Whiteboard:
Bug Depends on:    
Bug Blocks: 517055    
Attachments:
Description Flags
OS scaling enabled. Visible difference between line width 0 and 1. Rectangles drawn with gap between them.
none
Scaling disabled. No difference between visual line width of 0 and 1. Rectangles are directly drawn next to each other
none
Code used for testing the issue.
none
Additional samples
none
Additional sample code
none
Snippet: Mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value.
none
Screen-shot: Mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value. none

Description Lothar Lattermann CLA 2017-09-05 13:42:24 EDT
Created attachment 270083 [details]
OS scaling enabled. Visible difference between line width 0 and 1. Rectangles drawn with gap between them.

When a line is drawn with the GC in a HiDPI/Windows 10 environment with OS scaling enabled and the width of the line is set to 0 the acutal drawn line width is not scaled. When the line width is set to 1 the line width howver is scaled. Thus the visual end result between machines with or without OS scaling differs and is consequently not portable. 

We attached two screenshots to visualize the problem with scaling enabled (osScaling200percent.png) as well as the code we tested the issue with. We also attached a screen shot run the same test with scaling disabled (osScalingOff.png) to show the difference.


We think this is a bug because on a machine without OS scaling the visual result using a line width 0 or 1 is the same. With scaling Rectangles will not be joint or lines cannot be drawn directly next to each other.


Using the switch -Dswt.autoScale has no effect with line width 0 and thus does not remedy the issue. The dimension of the rectangle will be scaled but not the line width. Using a line width >= 1, the line width will be scaled however.


We tested with Windows 10 (Version 1703 Build 15063.483) with a 3840x2160 resulution and 200% scaling (OS default with that resolution). The SWT Version used was 3.106.0.v20170608-0516 in Eclipse Oxygen.



When a line is drawn with the GC in a HiDPI/Windows 10 environment with OS scaling enabled and the width of the line is set to 0 the acutal drawn line width is not scaled. When the line width is set to 1 the line width howver is scaled. Thus the visual end result between machines with or without OS scaling differs and is consequently not portable.
Comment 1 Lothar Lattermann CLA 2017-09-05 13:43:52 EDT
Created attachment 270084 [details]
Scaling disabled. No difference between visual line width of 0 and 1. Rectangles are directly drawn next to each other
Comment 2 Lothar Lattermann CLA 2017-09-05 13:44:26 EDT
Created attachment 270085 [details]
Code used for testing the issue.
Comment 3 Niraj Modi CLA 2017-10-25 04:56:37 EDT
See in GC, lineWidth '0' is actually a hint, refer JavaDoc of GC.setLineWidth():
Note that line width of zero is used as a hint to indicate that the fastest possible line drawing algorithms should be used. This means that the output may be different from line width one. 

SWT doesn't claim that output for lineWidth 0 and 1 to be same or different.

(In reply to Lothar Lattermann from comment #1)
> Created attachment 270084 [details]
> Scaling disabled. No difference between visual line width of 0 and 1.
> Rectangles are directly drawn next to each other
This attachment is at 100% zoom, so both lineWidth 0 and 1 are treated as same i.e. 1(because 1 is the fastest to draw)

(In reply to Lothar Lattermann from comment #0)
> Created attachment 270083 [details]
> OS scaling enabled. Visible difference between line width 0 and 1.
> Rectangles drawn with gap between them.
This attachment is at 200% zoom, so SWT will autoScale the coordinates/width information as below:
- lineWidth 0 is treated as 1(which is fastest to draw)
- lineWidth 1 is treated as 2(which is autoScaled)
- lineWidth 2 is treated as 4(which is autoScaled)

This justifies the behavior mentioned in this bug.
Comment 4 Niraj Modi CLA 2017-10-25 05:05:54 EDT
(In reply to Lothar Lattermann from comment #2)
> Created attachment 270085 [details]
> Code used for testing the issue.

One more point, which I got to know from Lakshmi, regarding the overlapping rectangles drawn at lineWidth 2(issue seen on both 100% and 200% zoom), am precisely referring below code from your code snippet:
      gc.setLineWidth(2);
      gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_RED));
      gc.drawRectangle(10, 50, 20, 20); //rect1
      gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GREEN));
      gc.drawRectangle(31, 50, 20, 20); //rect2
------------------------------------------------------------------------------
Above client code is not correct for x position of second rectangle, which should have been calculated like below:
rect2.x = rect1.x + rect1.width + lineWdith
So, rect2.x = 10 + 20 + 2
which makes,  rect2.x = 32

Which this value fixed in client code, Overlapping problem is resolved.
Comment 5 Lothar Lattermann CLA 2018-01-22 18:46:35 EST
Created attachment 272365 [details]
Additional samples

(In reply to Niraj Modi from comment #4)

I think there are issues in regards to **scaling** and using the GC to draw directly. 

Two of those are:
1) Lines with line width 0, while not drawn thicker (scaled), use indeed up more than 
   1px space and consequently cannot be drawn adjacent to each other [1, 2, 3]. 
2) Regardless of width it is not possible to position lines exactly (on an exact pixel) 
   other than transforming lines. 
   This becomes a problem for example when lines are drawn together with filled objects 
   such as rectangles like custom widgets do (for example NatTable [4, 5, 6, 7) or when 
   lines with width 0 are to be drawn adjacent to each other [1]. 

The reason for 1) appears to be that while a width of 0 is not scaled 
**coordinates != 0 are always scaled**. 
Consequently a line with width = 0 and x1 = 0 and y1 = y2 = 1 and x2 = 70 
will be scaled to width = 0 and x1 = 0 and y1 = y2 = 2 and x2 = 140.
Thus the line is not scaled in width or transformed horizontally (x1 = 0) but transformed 
vertically (y1 = y2 = 2) and scaled in length (x2 = 140). As a result however, 
coordinates like y1 = y2 = 1 are not reachable directly.


The calculation are done at:

GC#setLineWidth(int lineWidth) {
    lineWidth = DPIUtil.autoScaleUp (drawable, lineWidth);
    setLineWidthInPixels(lineWidth);
}

Display#autoScaleUpUsingNativeDPI (int size) {
    if (nativeDeviceZoom == 100 || size == SWT.DEFAULT) return size;
    float nativeScaleFactor = nativeDeviceZoom / 100f;
    return Math.round (size * nativeScaleFactor);  // width being 0 returns 0; yet, 
}                                                  // coordinates != 0 are always 
                                                   // scaled/transformed ignorant of width
                                                   // -> lines with width 0 are partly 
                                                   // scaled/transformed

GC#drawLine (int x1, int y1, int x2, int y2) { // should this method only scale when 
    x1 = DPIUtil.autoScaleUp (drawable, x1);   // width > 0?  
    x2 = DPIUtil.autoScaleUp (drawable, x2);   // because coordinates are scaled devs
    y1 = DPIUtil.autoScaleUp (drawable, y1);   // cannot hit an exact pixel anymore
    y2 = DPIUtil.autoScaleUp (drawable, y2);
    drawLineInPixels(x1, y1, x2, y2);          // should this be API enabling developers
}                                              // create workarounds?

Please note: When scaling is turned off the above mentioned issues do not come into play 
             and everything works as expected/intended.
Please note: (To my knowledge) the API provides developers with no means to 
             adjust/influence the scaling. Only the VM switch swt.autoScale may be used. 
             For example developers could hint the direction in which direction the 
             width should grow or that only fonts may be scaled or even temporarily 
             disable scaling completely.
Please note: Whilst focusing on line drawing the issues apply to points, etc. as well. 
             Please see https://www.eclipse.org/forums/index.php/t/1088692/ for more 
             information.


What the image shows:
[1] line width 0
due to scaling lines cannot be drawn adjacent to each other
drawing a line at y +1 will results in the line "jumping" 

[2] line width 1
line can be drawn next to each other but will be transformed by the scaling factor.

[3] line width 0
using transformation lines can be drawn adjacent to each other because we can use float values such as .5 .


As an example how NatTale is suffering from those issues we provide examples 4 - 7. 
With scaling enabled it is not possible (without transform) to draw a line filling the gap between rectangles. 

[4] line width 0
line does not fill the gap between 2 rectangles which are drawn 1 pixel below each other.
it is not possible to draw to line below each other either as a workaround.

[5] line width 1
line is scaled but cannot be drawn exactly in the gap between the two rectangles because the line grows 
to the outside/up

[6] line width 0
same as [4] but with ++y to emphasize that the line is "jumping" and that consequently it is not possible 
to fill the gap between 2 rectangles which are drawn 1 pixel below each other

[7] line width 1
same as [5] but with ++y to emphasize that the line is "jumping" and that consequently it is not possible 
to fill the gap between 2 rectangles which are drawn 1 pixel below each other


Those issues make it nearly impossible/too expensive to create custom widgets which can be used in non-scaled and scaled environments. 
The Eclipse IDE suffers from this as well whenever it uses a custom drawn widget like the tabs for example.
Comment 6 Lothar Lattermann CLA 2018-01-22 18:48:44 EST
Created attachment 272366 [details]
Additional sample code
Comment 7 Lothar Lattermann CLA 2018-01-22 18:55:20 EST
(In reply to Niraj Modi from comment #4)
> (In reply to Lothar Lattermann from comment #2)
> > Created attachment 270085 [details]
> > Code used for testing the issue.
> 
> One more point, which I got to know from Lakshmi, regarding the overlapping
> rectangles drawn at lineWidth 2(issue seen on both 100% and 200% zoom), am
> precisely referring below code from your code snippet:
>       gc.setLineWidth(2);
>       gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_RED));
>       gc.drawRectangle(10, 50, 20, 20); //rect1
>       gc.setForeground(gc.getDevice().getSystemColor(SWT.COLOR_GREEN));
>       gc.drawRectangle(31, 50, 20, 20); //rect2
> -----------------------------------------------------------------------------
> -
> Above client code is not correct for x position of second rectangle, which
> should have been calculated like below:
> rect2.x = rect1.x + rect1.width + lineWdith
> So, rect2.x = 10 + 20 + 2
> which makes,  rect2.x = 32
> 
> Which this value fixed in client code, Overlapping problem is resolved.

We will open anther ticket to describe this issue in more detail:
In short: developers cannot hint in which direction a value should be scaled/grow (for example left to right or top to bottom) 
The way the current code works a line with width 1 and y1 = y2 = 0 will be scaled outside of the visible area for example and thus be cut off.
Comment 8 Niraj Modi CLA 2018-02-01 08:23:20 EST
Created attachment 272494 [details]
Snippet: Mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value.

(In reply to Lothar Lattermann from comment #7)
> We will open anther ticket to describe this issue in more detail:
> In short: developers cannot hint in which direction a value should be
> scaled/grow (for example left to right or top to bottom) 
> The way the current code works a line with width 1 and y1 = y2 = 0 will be
> scaled outside of the visible area for example and thus be cut off.

I created one test snippet where-in am mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value.
Comment 9 Niraj Modi CLA 2018-02-01 08:42:27 EST
Created attachment 272496 [details]
Screen-shot: Mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value.

Since SWT coordinate system is integer based, we don't see any upfront solution to '0' lineWidth behavior at 200%zoom. Ideas are welcome here.
Comment 10 Lakshmi P Shanmugam CLA 2018-02-05 06:47:31 EST
(In reply to Niraj Modi from comment #9)
> Since SWT coordinate system is integer based, we don't see any upfront
> solution to '0' lineWidth behavior at 200%zoom. Ideas are welcome here.
Conrad, any ideas here?
Comment 11 Niraj Modi CLA 2018-05-07 07:33:28 EDT
(In reply to Niraj Modi from comment #9)
> Created attachment 272496 [details]
> Screen-shot: Mixing lineWidth=0 with lineWidth 1,2 at 200% zoom value.
> 
> Since SWT coordinate system is integer based, we don't see any upfront
> solution to '0' lineWidth behavior at 200%zoom. Ideas are welcome here.

There is no clear solution to lineWidth zero problem and hence it's not recommended to mix line width zero with other line widths in high DPI scenario.

For clarity, we will mention the same in JavaDoc of GC.setLineWidth() method.
Will share a gerrit shortly for 4.8 M7
Comment 12 Eclipse Genie CLA 2018-05-07 07:41:00 EDT
New Gerrit change created: https://git.eclipse.org/r/122254
Comment 14 Niraj Modi CLA 2018-05-07 08:00:18 EDT
(In reply to Eclipse Genie from comment #13)
> Gerrit change https://git.eclipse.org/r/122254 was merged to [master].
> Commit:
> http://git.eclipse.org/c/platform/eclipse.platform.swt.git/commit/
> ?id=9a393e8677dae90cf87b17188eb1104b7ca70f93

Resolving now.
Comment 15 Niraj Modi CLA 2018-05-09 02:28:37 EDT
Verified in I20180507-2205 on Win7.