This is an automated email from the ASF dual-hosted git repository.

ebakke pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new af00a5b  [NETBEANS-2646] Improve tabcontrol border appearance under 
fractional HiDPI scaling
af00a5b is described below

commit af00a5b578d484cda12e06258fd2830d882e0fee
Author: Eirik Bakke <eba...@ultorg.com>
AuthorDate: Thu Jun 6 11:36:22 2019 -0400

    [NETBEANS-2646] Improve tabcontrol border appearance under fractional HiDPI 
scaling
---
 .../swing/plaf/windows8/DPISafeBorder.java         | 135 +++++++++++++++++++++
 .../swing/plaf/windows8/Windows8LFCustoms.java     |   5 +-
 2 files changed, 137 insertions(+), 3 deletions(-)

diff --git 
a/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/DPISafeBorder.java
 
b/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/DPISafeBorder.java
new file mode 100644
index 0000000..010c93b
--- /dev/null
+++ 
b/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/DPISafeBorder.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.swing.plaf.windows8;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.geom.AffineTransform;
+import javax.swing.border.Border;
+import javax.swing.border.MatteBorder;
+
+/**
+ * A border similar to {@link MatteBorder}, but which avoids visual artifacts 
from rounding errors
+ * under non-integral HiDPI scaling factors (e.g. 150%).
+ *
+ * @author Eirik Bakke (eba...@ultorg.com)
+ */
+final class DPISafeBorder implements Border {
+    private final Insets insets;
+    private final Color color;
+
+    /**
+     * Create a new instance with the same semantics as that produced by
+     * {@link MatteBorder#MatteBorder(int, int, int, int, java.awt.Color)}.
+     *
+     * @param color may not be null
+     */
+    public static Border matte(int top, int left, int bottom, int right, Color 
color) {
+        return new DPISafeBorder(new Insets(top, left, bottom, right), color);
+    }
+
+    private DPISafeBorder(Insets insets, Color color) {
+        if (insets == null)
+            throw new NullPointerException();
+        if (color == null)
+            throw new NullPointerException();
+        this.insets = new Insets(insets.top, insets.left, insets.bottom, 
insets.right);
+        this.color = color;
+    }
+
+    @Override
+    public void paintBorder(Component c, Graphics g0, int x, int y, int width, 
int height) {
+        final Graphics2D g = (Graphics2D) g0;
+        final Color oldColor = g.getColor();
+        final AffineTransform oldTransform = g.getTransform();
+        g.translate(x, y);
+        final AffineTransform tx = g.getTransform();
+        final int txType = tx.getType();
+        final double scale;
+        /* On fractional DPI scaling factors, such as 150%, a logical pixel 
position, e.g. (5,0),
+        may end up being translated to a non-integral device pixel position, 
e.g. (7.5, 0). The same
+        goes for border thicknesses, which are specified in logical pixels. In 
this method, we do
+        all calculations and painting in device pixels, to avoid rounding 
errors causing visible
+        artifacts. On screens without HiDPI scaling, logical pixel values and 
device pixel values
+        are identical, and always integral (whole number) values. */
+        final int deviceWidth;
+        final int deviceHeight;
+        if (txType == AffineTransform.TYPE_UNIFORM_SCALE ||
+            txType == (AffineTransform.TYPE_UNIFORM_SCALE | 
AffineTransform.TYPE_TRANSLATION))
+        {
+              // HiDPI scaling is active.
+              scale = tx.getScaleX();
+              /* Round the starting (top-left) position up and the end 
(bottom-right) position down,
+              to ensure we are painting the border in an area that will not be 
painted over by an
+              adjacent component. */
+              int deviceX = (int) Math.ceil(tx.getTranslateX());
+              int deviceY = (int) Math.ceil(tx.getTranslateY());
+              int deviceXend = (int) (tx.getTranslateX() + width * scale);
+              int deviceYend = (int) (tx.getTranslateY() + height * scale);
+              deviceWidth = deviceXend - deviceX;
+              deviceHeight = deviceYend - deviceY;
+              /* Deactivate the HiDPI scaling transform so we can do paint 
operations in the device
+              pixel coordinate system instead of in the logical coordinate 
system. */
+              g.setTransform(new AffineTransform(1, 0, 0, 1, deviceX, 
deviceY));
+        } else {
+            scale = 1.0;
+            deviceWidth = width;
+            deviceHeight = height;
+        }
+        final int deviceLeft   = deviceBorderWidth(scale, insets.left);
+        final int deviceRight  = deviceBorderWidth(scale, insets.right);
+        final int deviceTop    = deviceBorderWidth(scale, insets.top);
+        final int deviceBottom = deviceBorderWidth(scale, insets.bottom);
+
+        g.setColor(color);
+
+        // Top border.
+        g.fillRect(0, 0, deviceWidth - deviceRight, deviceTop);
+        // Left border.
+        g.fillRect(0, deviceTop, deviceLeft, deviceHeight - deviceTop);
+        // Bottom border.
+        g.fillRect(deviceLeft, deviceHeight - deviceBottom, deviceWidth - 
deviceLeft, deviceBottom);
+        // Right border.
+        g.fillRect(deviceWidth - deviceRight, 0, deviceRight, deviceHeight - 
deviceBottom);
+
+        g.setTransform(oldTransform);
+        g.setColor(oldColor);
+    }
+
+    private int deviceBorderWidth(double scale, int logical) {
+        if (logical <= 0)
+            return 0;
+        return Math.max(1, (int) (scale * logical));
+    }
+
+    @Override
+    public Insets getBorderInsets(Component c) {
+        return new Insets(insets.top, insets.left, insets.bottom, 
insets.right);
+    }
+
+    @Override
+    public boolean isBorderOpaque() {
+        /* Set this to false to be safe, since we might not fill in the entire 
designated logical
+        area due to rounding errors in the conversion to device pixels. */
+        return false;
+    }
+}
diff --git 
a/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/Windows8LFCustoms.java
 
b/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/Windows8LFCustoms.java
index 851ffe7..f86cf5a 100644
--- 
a/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/Windows8LFCustoms.java
+++ 
b/platform/o.n.swing.plaf/src/org/netbeans/swing/plaf/windows8/Windows8LFCustoms.java
@@ -22,7 +22,6 @@ package org.netbeans.swing.plaf.windows8;
 import java.awt.*;
 import javax.swing.*;
 import javax.swing.border.EmptyBorder;
-import javax.swing.border.MatteBorder;
 import javax.swing.plaf.ColorUIResource;
 import org.netbeans.swing.plaf.LFCustoms;
 import org.netbeans.swing.plaf.util.GuaranteedValue;
@@ -223,12 +222,12 @@ public final class Windows8LFCustoms extends LFCustoms {
             //Borders for the tab control
             EDITOR_TAB_OUTER_BORDER, BorderFactory.createEmptyBorder(),
             EDITOR_TAB_CONTENT_BORDER,
-                new MatteBorder(0, 1, 1, 1, new Color(137, 140, 149)),
+                DPISafeBorder.matte(0, 1, 1, 1, new Color(137, 140, 149)),
             EDITOR_TAB_TABS_BORDER, BorderFactory.createEmptyBorder(),
 
             VIEW_TAB_OUTER_BORDER, BorderFactory.createEmptyBorder(),
             VIEW_TAB_CONTENT_BORDER,
-                new MatteBorder(0, 1, 1, 1, new Color(137, 140, 149)),
+                DPISafeBorder.matte(0, 1, 1, 1, new Color(137, 140, 149)),
             VIEW_TAB_TABS_BORDER, BorderFactory.createEmptyBorder(),
             };
         }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org
For additional commands, e-mail: commits-h...@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists

Reply via email to