From 511e237eacaaf2fa03336807269463438a9394ee Mon Sep 17 00:00:00 2001 From: Anton Tarasov Date: Fri, 22 Dec 2017 10:40:43 +0300 Subject: [PATCH] JRE-604 [fps] frame's client area is one pixel beneath frame's borders --- .../classes/java/awt/peer/WindowPeer.java | 7 ++ .../classes/javax/swing/RepaintManager.java | 101 +++++++++++++++++- .../classes/sun/java2d/SunGraphics2D.java | 1 + .../classes/sun/awt/windows/WWindowPeer.java | 8 ++ .../native/sun/java2d/d3d/D3DSurfaceData.cpp | 2 +- .../native/sun/java2d/opengl/WGLSurfaceData.c | 4 +- .../java2d/windows/GDIWindowSurfaceData.cpp | 2 +- .../native/sun/windows/awt_Component.cpp | 19 ++-- .../native/sun/windows/awt_Component.h | 4 - src/windows/native/sun/windows/awt_Window.cpp | 24 ++--- src/windows/native/sun/windows/awt_Window.h | 7 +- 11 files changed, 139 insertions(+), 40 deletions(-) diff --git a/src/share/classes/java/awt/peer/WindowPeer.java b/src/share/classes/java/awt/peer/WindowPeer.java index b44bfa00da..d9b2ffd35a 100644 --- a/src/share/classes/java/awt/peer/WindowPeer.java +++ b/src/share/classes/java/awt/peer/WindowPeer.java @@ -117,4 +117,11 @@ public interface WindowPeer extends ContainerPeer { * Instructs the peer to update the position of the security warning. */ void repositionSecurityWarning(); + + /** + * Returns the system insets (in the scale of the Window device) when available. + * + * @return the system insets or null + */ + default Insets getSysInsets() { return null; } } diff --git a/src/share/classes/javax/swing/RepaintManager.java b/src/share/classes/javax/swing/RepaintManager.java index 084ddd3bdb..75a2d5d81c 100644 --- a/src/share/classes/javax/swing/RepaintManager.java +++ b/src/share/classes/javax/swing/RepaintManager.java @@ -27,7 +27,10 @@ import java.awt.*; import java.awt.event.*; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; import java.awt.image.VolatileImage; +import java.awt.peer.WindowPeer; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; @@ -1564,11 +1567,70 @@ public void doubleBufferingChanged(JRootPane rootPane) { */ protected void paintDoubleBuffered(JComponent c, Image image, Graphics g, int clipX, int clipY, - int clipW, int clipH) { - if (image instanceof VolatileImage && isPixelsCopying(c, g)) { - paintDoubleBufferedFPScales(c, image, g, clipX, clipY, clipW, clipH); - } else { - paintDoubleBufferedImpl(c, image, g, clipX, clipY, clipW, clipH); + int clipW, int clipH) + { + Graphics gg = g; + // [tav] For the scaling graphics we need to compensate the toplevel insets rounding error + // to place [0, 0] of the client area in its correct device pixel. + if (g instanceof SunGraphics2D) { + SunGraphics2D sg = (SunGraphics2D)gg; + if (sg.transformState == SunGraphics2D.TRANSFORM_TRANSLATESCALE) { + Point2D err = getInsetsRoundingError(sg); + double errX = err.getX(); + double errY = err.getY(); + if (errX != 0 || errY != 0) { + gg = sg = (SunGraphics2D)sg.create(); + + // save the current tx + AffineTransform tx = sg.transform; + + // translate the constrain + Region constrainClip = sg.constrainClip; + Shape usrClip = sg.usrClip; + if (constrainClip != null) { + // SunGraphics2D.constrain(..) rounds down x/y, so to compensate we need to round up + int _errX = (int)Math.ceil(errX); + int _errY = (int)Math.ceil(errY); + if ((_errX | _errY) != 0) { + // drop everything to default + sg.constrainClip = null; + sg.usrClip = null; + sg.clipState = SunGraphics2D.CLIP_DEVICE; + sg.transform = new AffineTransform(); + sg.setDevClip(sg.getSurfaceData().getBounds()); + + Region r = constrainClip.getTranslatedRegion(_errX, _errY); + sg.constrain(r.getLoX(), r.getLoY(), r.getWidth(), r.getHeight()); + } + } + + // translate usrClip + if (usrClip != null) { + if (usrClip instanceof Rectangle2D) { + Rectangle2D u = (Rectangle2D)usrClip; + u.setRect(u.getX() + errX, u.getY() + errY, u.getWidth(), u.getHeight()); + } else { + usrClip = AffineTransform.getTranslateInstance(errX, errY).createTransformedShape(usrClip); + } + sg.transform = new AffineTransform(); + sg.setClip(usrClip); // constrain clip is already valid + } + + // finally translate the tx + AffineTransform newTx = AffineTransform.getTranslateInstance(errX - sg.constrainX, errY - sg.constrainY); + newTx.concatenate(tx); + sg.setTransform(newTx); + } + } + } + try { + if (image instanceof VolatileImage && isPixelsCopying(c, g)) { + paintDoubleBufferedFPScales(c, image, gg, clipX, clipY, clipW, clipH); + } else { + paintDoubleBufferedImpl(c, image, gg, clipX, clipY, clipW, clipH); + } + } finally { + if (gg != g) g.dispose(); } } @@ -1613,6 +1675,35 @@ private void paintDoubleBufferedImpl(JComponent c, Image image, } } + /** + * For the scaling graphics and a decorated toplevel as the destination, + * calculates the rounding error of the toplevel insets. + * + * @return the left/top insets rounding error, in device space + */ + private static Point2D getInsetsRoundingError(SunGraphics2D g) { + Point2D.Double err = new Point2D.Double(0, 0); + if (g.transformState >= SunGraphics2D.TRANSFORM_TRANSLATESCALE) { + Object dst = g.getSurfaceData().getDestination(); + if (dst instanceof Frame && !((Frame)dst).isUndecorated() || + dst instanceof Dialog && !((Dialog)dst).isUndecorated()) + { + Window wnd = (Window)dst; + WindowPeer peer = (WindowPeer)wnd.getPeer(); + Insets sysInsets = peer != null ? peer.getSysInsets() : null; + if (sysInsets != null) { + Insets insets = wnd.getInsets(); + // insets.left/top is a scaled down rounded value + // insets.left/top * tx.scale is a scaled up value (which contributes to graphics translate) + // sysInsets.left/top is the precise system value + err.x = sysInsets.left - insets.left * g.transform.getScaleX(); + err.y = sysInsets.top - insets.top * g.transform.getScaleY(); + } + } + } + return err; + } + private void paintDoubleBufferedFPScales(JComponent c, Image image, Graphics g, int clipX, int clipY, int clipW, int clipH) { diff --git a/src/share/classes/sun/java2d/SunGraphics2D.java b/src/share/classes/sun/java2d/SunGraphics2D.java index 1b1428c9b7..2426aed777 100644 --- a/src/share/classes/sun/java2d/SunGraphics2D.java +++ b/src/share/classes/sun/java2d/SunGraphics2D.java @@ -372,6 +372,7 @@ public void constrain(int x, int y, int w, int h, Region region) { // changes parameters according to the current scale and translate. final double scaleX = transform.getScaleX(); final double scaleY = transform.getScaleY(); + // [tav] rounding down affects aligning by insets in RepaintManager.paintDoubleBuffered x = constrainX = (int) transform.getTranslateX(); y = constrainY = (int) transform.getTranslateY(); w = Region.dimAdd(x, Region.clipScale(w, scaleX)); diff --git a/src/windows/classes/sun/awt/windows/WWindowPeer.java b/src/windows/classes/sun/awt/windows/WWindowPeer.java index c8f00fcf66..95e066f253 100644 --- a/src/windows/classes/sun/awt/windows/WWindowPeer.java +++ b/src/windows/classes/sun/awt/windows/WWindowPeer.java @@ -81,6 +81,8 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer, */ private WindowListener windowListener; + private Insets sysInsets; // set from native updateInsets + /** * Initialize JNI field IDs */ @@ -177,6 +179,7 @@ public void setResizable(boolean resizable) { void initialize() { super.initialize(); + sysInsets = (Insets)insets_.clone(); updateInsets(insets_); Font f = ((Window)target).getFont(); @@ -278,6 +281,11 @@ public void show() { // state. native void updateInsets(Insets i); + @Override + public Insets getSysInsets() { + return (Insets)sysInsets.clone(); + } + static native int getSysMinWidth(); static native int getSysMinHeight(); static native int getSysIconWidth(); diff --git a/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp b/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp index c361d6b32b..1ede03bf56 100644 --- a/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp +++ b/src/windows/native/sun/java2d/d3d/D3DSurfaceData.cpp @@ -338,7 +338,7 @@ JNICALL Java_sun_java2d_d3d_D3DSurfaceData_initFlipBackbuffer return JNI_FALSE; } - pPeer->GetAlignedInsets(&r); + pPeer->GetInsets(&r); d3dsdo->xoff = -r.left; d3dsdo->yoff = -r.top; diff --git a/src/windows/native/sun/java2d/opengl/WGLSurfaceData.c b/src/windows/native/sun/java2d/opengl/WGLSurfaceData.c index 7e9aede226..d316a63ead 100644 --- a/src/windows/native/sun/java2d/opengl/WGLSurfaceData.c +++ b/src/windows/native/sun/java2d/opengl/WGLSurfaceData.c @@ -51,7 +51,7 @@ extern void AwtWindow_UpdateWindow(JNIEnv *env, jobject peer, extern HBITMAP BitmapUtil_CreateBitmapFromARGBPre(int width, int height, int srcStride, int* imageData); -extern void AwtComponent_GetAlignedInsets(JNIEnv *env, jobject peer, RECT *insets); +extern void AwtComponent_GetInsets(JNIEnv *env, jobject peer, RECT *insets); extern void OGLSD_SetNativeDimensions(JNIEnv *env, OGLSDOps *oglsdo, jint w, jint h); @@ -89,7 +89,7 @@ Java_sun_java2d_opengl_WGLSurfaceData_initOps(JNIEnv *env, jobject wglsd, oglsdo->needsInit = JNI_TRUE; if (peer != NULL) { RECT insets; - AwtComponent_GetAlignedInsets(env, peer, &insets); + AwtComponent_GetInsets(env, peer, &insets); oglsdo->xOffset = -insets.left; oglsdo->yOffset = -insets.bottom; } else { diff --git a/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp b/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp index 883e7c1216..3e706c43ce 100644 --- a/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp +++ b/src/windows/native/sun/java2d/windows/GDIWindowSurfaceData.cpp @@ -531,7 +531,7 @@ GDIWindowSurfaceData_GetWindow(JNIEnv *env, GDIWinSDOps *wsdo) "GDIWindowSurfaceData_GetWindow: null component"); return (HWND) NULL; } - comp->GetAlignedInsets(&wsdo->insets); + comp->GetInsets(&wsdo->insets); window = comp->GetHWnd(); if (::IsWindow(window) == FALSE) { J2dRlsTraceLn(J2D_TRACE_ERROR, diff --git a/src/windows/native/sun/windows/awt_Component.cpp b/src/windows/native/sun/windows/awt_Component.cpp index 598b52413d..2642b23471 100644 --- a/src/windows/native/sun/windows/awt_Component.cpp +++ b/src/windows/native/sun/windows/awt_Component.cpp @@ -136,8 +136,8 @@ struct SetRectangularShapeStruct { jint x1, x2, y1, y2; jobject region; }; -// Struct for _GetAlignedInsets function -struct GetAlignedInsetsStruct { +// Struct for _GetInsets function +struct GetInsetsStruct { jobject window; RECT *insets; }; @@ -989,8 +989,7 @@ void AwtComponent::Reshape(int x, int y, int w, int h) // [tav] Handle the fact that an owned window is most likely positioned relative to its owner, and it may // require pixel-perfect alignment. For that, compensate rounding errors (caused by converting from the device - // space to the integer user space and back) for the owner's origin and for the owner's client area origin - // (see Window::GetAlignedInsets). + // space to the integer user space and back) for the owner's origin and for the owner's client area origin. if (IsTopLevel() && parent != NULL && (device->GetScaleX() > 1 || device->GetScaleY() > 1)) { @@ -6449,11 +6448,11 @@ AwtComponent_GetHWnd(JNIEnv *env, jlong pData) return p->GetHWnd(); } -static void _GetAlignedInsets(void* param) +static void _GetInsets(void* param) { JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2); - GetAlignedInsetsStruct *gis = (GetAlignedInsetsStruct *)param; + GetInsetsStruct *gis = (GetInsetsStruct *)param; jobject self = gis->window; gis->insets->left = gis->insets->top = @@ -6463,7 +6462,7 @@ static void _GetAlignedInsets(void* param) JNI_CHECK_PEER_GOTO(self, ret); AwtComponent *component = (AwtComponent *)pData; - component->GetAlignedInsets(gis->insets); + component->GetInsets(gis->insets); ret: env->DeleteGlobalRef(self); @@ -6474,15 +6473,15 @@ static void _GetAlignedInsets(void* param) * This method is called from the WGL pipeline when it needs to retrieve * the insets associated with a ComponentPeer's C++ level object. */ -void AwtComponent_GetAlignedInsets(JNIEnv *env, jobject peer, RECT *insets) +void AwtComponent_GetInsets(JNIEnv *env, jobject peer, RECT *insets) { TRY; - GetAlignedInsetsStruct *gis = new GetAlignedInsetsStruct; + GetInsetsStruct *gis = new GetInsetsStruct; gis->window = env->NewGlobalRef(peer); gis->insets = insets; - AwtToolkit::GetInstance().InvokeFunction(_GetAlignedInsets, gis); + AwtToolkit::GetInstance().InvokeFunction(_GetInsets, gis); // global refs and mds are deleted in _UpdateWindow CATCH_BAD_ALLOC; diff --git a/src/windows/native/sun/windows/awt_Component.h b/src/windows/native/sun/windows/awt_Component.h index e588c2ccd3..85e6faaf82 100644 --- a/src/windows/native/sun/windows/awt_Component.h +++ b/src/windows/native/sun/windows/awt_Component.h @@ -188,10 +188,6 @@ class AwtComponent : public AwtObject { VERIFY(::SetRectEmpty(rect)); } - virtual void GetAlignedInsets(RECT* rect) { - VERIFY(::SetRectEmpty(rect)); - } - BOOL IsVisible() { return m_visible;}; HDC GetDCFromComponent(); diff --git a/src/windows/native/sun/windows/awt_Window.cpp b/src/windows/native/sun/windows/awt_Window.cpp index 7256b2954e..a5fd80607c 100644 --- a/src/windows/native/sun/windows/awt_Window.cpp +++ b/src/windows/native/sun/windows/awt_Window.cpp @@ -165,6 +165,7 @@ jfieldID AwtWindow::sysYID; jfieldID AwtWindow::sysWID; jfieldID AwtWindow::sysHID; jfieldID AwtWindow::windowTypeID; +jfieldID AwtWindow::sysInsetsID; jmethodID AwtWindow::getWarningStringMID; jmethodID AwtWindow::calculateSecurityWarningPositionMID; @@ -1435,29 +1436,26 @@ BOOL AwtWindow::UpdateInsets(jobject insets) jobject peerInsets = (env)->GetObjectField(peer, AwtPanel::insets_ID); DASSERT(!safe_ExceptionOccurred(env)); + jobject peerSysInsets = (env)->GetObjectField(peer, AwtWindow::sysInsetsID); + DASSERT(!safe_ExceptionOccurred(env)); + int user_left = ScaleDownX(m_insets.left); int user_top = ScaleDownY(m_insets.top); int user_right = ScaleDownX(m_insets.right); int user_bottom = ScaleDownY(m_insets.bottom); - // [tav] Align the insets in the device space with their transformed (and rounded to int) values in the user space. - // This will align the origin of the non-client area with its physical origin after transforming back to the - // device space. This will avoid gaps b/w the window's rendered content and the window's upper/left borders. - // However, even so the size of the non-client area, transformed back from the user space, may not exactly match - // the device size of the client area when the the window is resized natively (with mouse). In that case the - // right/bottom gaps will be filled with the window background color, which is configured in java and is - // expected to not contrast with the overall UI. - m_aligned_insets.left = ScaleUpX(user_left); - m_aligned_insets.top = ScaleUpY(user_top); - m_aligned_insets.right = ScaleUpX(user_right); - m_aligned_insets.bottom = ScaleUpY(user_bottom); - if (peerInsets != NULL) { // may have been called during creation (env)->SetIntField(peerInsets, AwtInsets::topID, user_top); (env)->SetIntField(peerInsets, AwtInsets::bottomID, user_bottom); (env)->SetIntField(peerInsets, AwtInsets::leftID, user_left); (env)->SetIntField(peerInsets, AwtInsets::rightID, user_right); } + if (peerSysInsets != NULL) { + (env)->SetIntField(peerSysInsets, AwtInsets::topID, m_insets.top); + (env)->SetIntField(peerSysInsets, AwtInsets::bottomID, m_insets.bottom); + (env)->SetIntField(peerSysInsets, AwtInsets::leftID, m_insets.left); + (env)->SetIntField(peerSysInsets, AwtInsets::rightID, m_insets.right); + } /* Get insets into the Inset object (if any) that was passed */ if (insets != NULL) { (env)->SetIntField(insets, AwtInsets::topID, user_top); @@ -3242,6 +3240,8 @@ Java_sun_awt_windows_WWindowPeer_initIDs(JNIEnv *env, jclass cls) CHECK_NULL(AwtWindow::sysWID = env->GetFieldID(cls, "sysW", "I")); CHECK_NULL(AwtWindow::sysHID = env->GetFieldID(cls, "sysH", "I")); + CHECK_NULL(AwtWindow::sysInsetsID = env->GetFieldID(cls, "sysInsets", "Ljava/awt/Insets;")); + AwtWindow::windowTypeID = env->GetFieldID(cls, "windowType", "Ljava/awt/Window$Type;"); diff --git a/src/windows/native/sun/windows/awt_Window.h b/src/windows/native/sun/windows/awt_Window.h index 4a3e433ccd..e118dfcbed 100644 --- a/src/windows/native/sun/windows/awt_Window.h +++ b/src/windows/native/sun/windows/awt_Window.h @@ -63,6 +63,8 @@ class AwtWindow : public AwtCanvas { static jfieldID sysWID; static jfieldID sysHID; + static jfieldID sysInsetsID; + static jfieldID windowTypeID; static jmethodID getWarningStringMID; @@ -104,10 +106,6 @@ class AwtWindow : public AwtCanvas { VERIFY(::CopyRect(rect, &m_insets)); } - virtual void GetAlignedInsets(RECT* rect) { - VERIFY(::CopyRect(rect, &m_aligned_insets)); - } - /* to make embedded frames easier */ virtual BOOL IsEmbeddedFrame() { return FALSE;} @@ -272,7 +270,6 @@ class AwtWindow : public AwtCanvas { RECT m_insets; /* a cache of the insets being used */ RECT m_old_insets; /* help determine if insets change */ - RECT m_aligned_insets; /* transformed to user space and back to device scape */ POINT m_sizePt; /* the last value of WM_SIZE */ RECT m_warningRect; /* The window's warning banner area, if any. */ AwtFrame *m_owningFrameDialog; /* The nearest Frame/Dialog which owns us */