8043869: [macosx] java -splash does not honor @2x hi dpi notation for retina support
authoralexsch
Wed, 25 Jun 2014 19:10:32 +0400
changeset 25552 96abb01815f6
parent 25210 d2224e06e44f
child 25553 1082bb71e6c5
8043869: [macosx] java -splash does not honor @2x hi dpi notation for retina support Reviewed-by: pchelko, anthony, ksrini
jdk/make/mapfiles/libsplashscreen/mapfile-vers
jdk/src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m
jdk/src/share/bin/java.c
jdk/src/share/bin/splashscreen.h
jdk/src/share/bin/splashscreen_stubs.c
jdk/src/share/classes/java/awt/SplashScreen.java
jdk/src/share/native/sun/awt/splashscreen/java_awt_SplashScreen.c
jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.c
jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.h
jdk/src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c
jdk/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c
jdk/test/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java
--- a/jdk/make/mapfiles/libsplashscreen/mapfile-vers	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/make/mapfiles/libsplashscreen/mapfile-vers	Wed Jun 25 19:10:32 2014 +0400
@@ -35,6 +35,7 @@
                 Java_java_awt_SplashScreen__1getImageFileName;
                 Java_java_awt_SplashScreen__1getImageJarName;
                 Java_java_awt_SplashScreen__1setImageData;
+                Java_java_awt_SplashScreen__1getScaleFactor;
 
 		SplashLoadMemory;
 		SplashLoadFile;
--- a/jdk/src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m	Wed Jun 25 19:10:32 2014 +0400
@@ -125,6 +125,39 @@
     return buf;
 }
 
+char* SplashGetScaledImageName(const char* jar, const char* file,
+                               float *scaleFactor) {
+    NSAutoreleasePool *pool = [NSAutoreleasePool new];
+    *scaleFactor = 1;
+    char* scaledFile = nil;
+    float screenScaleFactor = [SplashNSScreen() backingScaleFactor];
+    
+    if (screenScaleFactor > 1) {
+        NSString *fileName = [NSString stringWithUTF8String: file];
+        NSUInteger length = [fileName length];
+        NSRange range = [fileName rangeOfString: @"."
+                                        options:NSBackwardsSearch];
+        NSUInteger dotIndex = range.location;
+        NSString *fileName2x = nil;
+        
+        if (dotIndex == NSNotFound) {
+            fileName2x = [fileName stringByAppendingString: @"@2x"];
+        } else {
+            fileName2x = [fileName substringToIndex: dotIndex];
+            fileName2x = [fileName2x stringByAppendingString: @"@2x"];
+            fileName2x = [fileName2x stringByAppendingString:
+                          [fileName substringFromIndex: dotIndex]];
+        }
+        
+        if ((fileName2x != nil) && (jar || [[NSFileManager defaultManager]
+                    fileExistsAtPath: fileName2x])){
+            *scaleFactor = 2;
+            scaledFile = strdup([fileName2x UTF8String]);
+        }
+    }
+    [pool drain];
+    return scaledFile;
+}
 
 void
 SplashInitPlatform(Splash * splash) {
@@ -132,7 +165,7 @@
 
     splash->maskRequired = 0;
 
-
+    
     //TODO: the following is too much of a hack but should work in 90% cases.
     //      besides we don't use device-dependant drawing, so probably
     //      that's very fine indeed
@@ -225,7 +258,15 @@
         [image setBackgroundColor: [NSColor clearColor]];
 
         [image addRepresentation: rep];
-
+        float scaleFactor = splash->scaleFactor;
+        if (scaleFactor > 0 && scaleFactor != 1) {
+            [image setScalesWhenResized:YES];
+            NSSize size = [image size];
+            size.width /= scaleFactor;
+            size.height /= scaleFactor;
+            [image setSize: size];
+        }
+        
         NSImageView * view = [[NSImageView alloc] init];
 
         [view setImage: image];
--- a/jdk/src/share/bin/java.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/bin/java.c	Wed Jun 25 19:10:32 2014 +0400
@@ -1816,20 +1816,48 @@
     const char *jar_name = getenv(SPLASH_JAR_ENV_ENTRY);
     const char *file_name = getenv(SPLASH_FILE_ENV_ENTRY);
     int data_size;
-    void *image_data;
+    void *image_data = NULL;
+    float scale_factor = 1;
+    char *scaled_splash_name = NULL;
+
+    if (file_name == NULL){
+        return;
+    }
+
+    scaled_splash_name = DoSplashGetScaledImageName(
+                        jar_name, file_name, &scale_factor);
     if (jar_name) {
-        image_data = JLI_JarUnpackFile(jar_name, file_name, &data_size);
+
+        if (scaled_splash_name) {
+            image_data = JLI_JarUnpackFile(
+                    jar_name, scaled_splash_name, &data_size);
+        }
+
+        if (!image_data) {
+            scale_factor = 1;
+            image_data = JLI_JarUnpackFile(
+                            jar_name, file_name, &data_size);
+        }
         if (image_data) {
             DoSplashInit();
+            DoSplashSetScaleFactor(scale_factor);
             DoSplashLoadMemory(image_data, data_size);
             JLI_MemFree(image_data);
         }
-    } else if (file_name) {
+    } else {
         DoSplashInit();
-        DoSplashLoadFile(file_name);
-    } else {
-        return;
+        if (scaled_splash_name) {
+            DoSplashSetScaleFactor(scale_factor);
+            DoSplashLoadFile(scaled_splash_name);
+        } else {
+            DoSplashLoadFile(file_name);
+        }
     }
+
+    if (scaled_splash_name) {
+        JLI_MemFree(scaled_splash_name);
+    }
+
     DoSplashSetFileJarName(file_name, jar_name);
 
     /*
--- a/jdk/src/share/bin/splashscreen.h	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/bin/splashscreen.h	Wed Jun 25 19:10:32 2014 +0400
@@ -29,3 +29,6 @@
 void    DoSplashInit(void);
 void    DoSplashClose(void);
 void    DoSplashSetFileJarName(const char* fileName, const char* jarName);
+void    DoSplashSetScaleFactor(float scaleFactor);
+char*   DoSplashGetScaledImageName(const char* jarName, const char* fileName,
+                                    float* scaleFactor);
--- a/jdk/src/share/bin/splashscreen_stubs.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/bin/splashscreen_stubs.c	Wed Jun 25 19:10:32 2014 +0400
@@ -37,6 +37,9 @@
 typedef void (*SplashClose_t)(void);
 typedef void (*SplashSetFileJarName_t)(const char* fileName,
                                        const char* jarName);
+typedef void (*SplashSetScaleFactor_t)(float scaleFactor);
+typedef char* (*SplashGetScaledImageName_t)(const char* fileName,
+                        const char* jarName, float* scaleFactor);
 
 /*
  * This macro invokes a function from the shared lib.
@@ -58,11 +61,11 @@
 #define INVOKEV(name) _INVOKE(name, ,;)
 
 int     DoSplashLoadMemory(void* pdata, int size) {
-    INVOKE(SplashLoadMemory,0)(pdata, size);
+    INVOKE(SplashLoadMemory, NULL)(pdata, size);
 }
 
 int     DoSplashLoadFile(const char* filename) {
-    INVOKE(SplashLoadFile,0)(filename);
+    INVOKE(SplashLoadFile, NULL)(filename);
 }
 
 void    DoSplashInit(void) {
@@ -76,3 +79,12 @@
 void    DoSplashSetFileJarName(const char* fileName, const char* jarName) {
     INVOKEV(SplashSetFileJarName)(fileName, jarName);
 }
+
+void    DoSplashSetScaleFactor(float scaleFactor) {
+    INVOKEV(SplashSetScaleFactor)(scaleFactor);
+}
+
+char*    DoSplashGetScaledImageName(const char* fileName, const char* jarName,
+                                    float* scaleFactor) {
+    INVOKE(SplashGetScaledImageName, NULL)(fileName, jarName, scaleFactor);
+}
\ No newline at end of file
--- a/jdk/src/share/classes/java/awt/SplashScreen.java	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/classes/java/awt/SplashScreen.java	Wed Jun 25 19:10:32 2014 +0400
@@ -245,7 +245,14 @@
     public Rectangle getBounds() throws IllegalStateException {
         synchronized (SplashScreen.class) {
             checkVisible();
-            return _getBounds(splashPtr);
+            float scale = _getScaleFactor(splashPtr);
+            Rectangle bounds = _getBounds(splashPtr);
+            assert scale > 0;
+            if (scale > 0 && scale != 1) {
+                bounds.setSize((int) (bounds.getWidth() / scale),
+                        (int) (bounds.getWidth() / scale));
+            }
+            return bounds;
         }
     }
 
@@ -287,10 +294,19 @@
     public Graphics2D createGraphics() throws IllegalStateException {
         synchronized (SplashScreen.class) {
             if (image==null) {
-                Dimension dim = getSize();
-                image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
+                // get unscaled splash image size
+                Dimension dim = _getBounds(splashPtr).getSize();
+                image = new BufferedImage(dim.width, dim.height,
+                        BufferedImage.TYPE_INT_ARGB);
             }
-            return image.createGraphics();
+            float scale = _getScaleFactor(splashPtr);
+            Graphics2D g = image.createGraphics();
+            assert (scale > 0);
+            if (scale <= 0) {
+                scale = 1;
+            }
+            g.scale(scale, scale);
+            return g;
         }
     }
 
@@ -401,5 +417,6 @@
     private native static String _getImageFileName(long splashPtr);
     private native static String _getImageJarName(long SplashPtr);
     private native static boolean _setImageData(long SplashPtr, byte[] data);
+    private native static float _getScaleFactor(long SplashPtr);
 
 };
--- a/jdk/src/share/native/sun/awt/splashscreen/java_awt_SplashScreen.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/native/sun/awt/splashscreen/java_awt_SplashScreen.c	Wed Jun 25 19:10:32 2014 +0400
@@ -220,3 +220,18 @@
     (*env)->ReleaseByteArrayElements(env, data, pBytes, JNI_ABORT);
     return rc ? JNI_TRUE : JNI_FALSE;
 }
+
+/*
+ * Class:     java_awt_SplashScreen
+ * Method:    _getScaleFactor
+ * Signature: (J)F
+ */
+JNIEXPORT jfloat JNICALL Java_java_awt_SplashScreen__1getScaleFactor
+(JNIEnv *env, jclass thisClass, jlong jsplash)
+{
+    Splash *splash = (Splash *) jlong_to_ptr(jsplash);
+    if (!splash) {
+        return 1;
+    }
+    return splash->scaleFactor;
+}
\ No newline at end of file
--- a/jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.c	Wed Jun 25 19:10:32 2014 +0400
@@ -59,6 +59,7 @@
 
     memset(splash, 0, sizeof(Splash));
     splash->currentFrame = -1;
+    splash->scaleFactor = 1;
     initFormat(&splash->imageFormat, QUAD_RED_MASK, QUAD_GREEN_MASK,
         QUAD_BLUE_MASK, QUAD_ALPHA_MASK);
     SplashInitPlatform(splash);
@@ -101,6 +102,13 @@
     SplashSetFileJarName(NULL, NULL);
 }
 
+SPLASHEXPORT void
+SplashSetScaleFactor(float scaleFactor)
+{
+    Splash *splash = SplashGetInstance();
+    splash->scaleFactor = scaleFactor;
+}
+
 void
 SplashDone(Splash * splash)
 {
--- a/jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.h	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/share/native/sun/awt/splashscreen/splashscreen_impl.h	Wed Jun 25 19:10:32 2014 +0400
@@ -35,6 +35,9 @@
 SPLASHEXPORT void SplashInit(void);
 SPLASHEXPORT void SplashClose(void);
 
+SPLASHEXPORT void SplashSetScaleFactor(float);
+SPLASHEXPORT char* SplashGetScaledImageName(const char*, const char*, float*);
+
 SPLASHEXPORT void
 SplashSetFileJarName(const char* fileName, const char* jarName);
 
@@ -79,6 +82,7 @@
     int         fileNameLen;
     char*       jarName;        /* stored in 16-bit unicode (jchars) */
     int         jarNameLen;
+    float       scaleFactor;
 #if defined(WITH_WIN32)
     BOOL isLayered;
     HWND hWnd;
@@ -115,6 +119,8 @@
 
 unsigned SplashTime();
 char* SplashConvertStringAlloc(const char* in, int *size);
+char* SplashGetScaledImageName(const char* jarName,
+                               const char* fileName, float *scaleFactor);
 
 void SplashLock(Splash * splash);
 void SplashUnlock(Splash * splash);
@@ -138,6 +144,7 @@
 void SplashUpdateScreenData(Splash * splash);
 
 void SplashCleanup(Splash * splash);
+void SplashSetScaleFactor(float scaleFactor);
 
 
 typedef struct SplashStream {
--- a/jdk/src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c	Wed Jun 25 19:10:32 2014 +0400
@@ -794,3 +794,11 @@
 SplashReconfigure(Splash * splash) {
     sendctl(splash, SPLASHCTL_RECONFIGURE);
 }
+
+SPLASHEXPORT char*
+SplashGetScaledImageName(const char* jarName, const char* fileName,
+                           float *scaleFactor)
+{
+    *scaleFactor = 1;
+    return NULL;
+}
--- a/jdk/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c	Tue Jun 24 12:27:37 2014 +0400
+++ b/jdk/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c	Wed Jun 25 19:10:32 2014 +0400
@@ -563,3 +563,11 @@
 {
     PostMessage(splash->hWnd, WM_SPLASHRECONFIGURE, 0, 0);
 }
+
+SPLASHEXPORT char*
+SplashGetScaledImageName(const char* jarName, const char* fileName,
+                           float *scaleFactor)
+{
+    *scaleFactor = 1;
+    return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java	Wed Jun 25 19:10:32 2014 +0400
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Color;
+import java.awt.Dialog;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Panel;
+import java.awt.Rectangle;
+import java.awt.Robot;
+import java.awt.SplashScreen;
+import java.awt.Window;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import javax.imageio.ImageIO;
+import sun.java2d.SunGraphics2D;
+
+/**
+ * @test
+ * @bug 8043869
+ * @author Alexander Scherbatiy
+ * @summary [macosx] java -splash does not honor 2x hi dpi notation for retina
+ * support
+ * @run main MultiResolutionSplashTest GENERATE_IMAGES
+ * @run main/othervm -splash:splash1.png MultiResolutionSplashTest TEST_SPLASH 0
+ * @run main/othervm -splash:splash2 MultiResolutionSplashTest TEST_SPLASH 1
+ * @run main/othervm -splash:splash3. MultiResolutionSplashTest TEST_SPLASH 2
+ */
+public class MultiResolutionSplashTest {
+
+    private static final int IMAGE_WIDTH = 300;
+    private static final int IMAGE_HEIGHT = 200;
+
+    private static final ImageInfo[] tests = {
+        new ImageInfo("splash1.png", "splash1@2x.png", Color.BLUE, Color.GREEN),
+        new ImageInfo("splash2", "splash2@2x", Color.WHITE, Color.BLACK),
+        new ImageInfo("splash3.", "splash3@2x.", Color.YELLOW, Color.RED)
+    };
+
+    public static void main(String[] args) throws Exception {
+
+        String test = args[0];
+
+        switch (test) {
+            case "GENERATE_IMAGES":
+                generateImages();
+                break;
+            case "TEST_SPLASH":
+                int index = Integer.parseInt(args[1]);
+                testSplash(tests[index]);
+                break;
+            default:
+                throw new RuntimeException("Unknown test: " + test);
+        }
+    }
+
+    static void testSplash(ImageInfo test) throws Exception {
+        SplashScreen splashScreen = SplashScreen.getSplashScreen();
+
+        if (splashScreen == null) {
+            throw new RuntimeException("Splash screen is not shown!");
+        }
+
+        Graphics2D g = splashScreen.createGraphics();
+        Rectangle splashBounds = splashScreen.getBounds();
+        int screenX = (int) splashBounds.getCenterX();
+        int screenY = (int) splashBounds.getCenterY();
+
+        Robot robot = new Robot();
+        Color splashScreenColor = robot.getPixelColor(screenX, screenY);
+
+        float scaleFactor = getScaleFactor();
+        Color testColor = (1 < scaleFactor) ? test.color2x : test.color1x;
+
+        if (!testColor.equals(splashScreenColor)) {
+            throw new RuntimeException(
+                    "Image with wrong resolution is used for splash screen!");
+        }
+    }
+
+    static float getScaleFactor() {
+
+        final Dialog dialog = new Dialog((Window) null);
+        dialog.setSize(100, 100);
+        dialog.setModal(true);
+        final float[] scaleFactors = new float[1];
+        Panel panel = new Panel() {
+
+            @Override
+            public void paint(Graphics g) {
+                float scaleFactor = 1;
+                if (g instanceof SunGraphics2D) {
+                    scaleFactor = ((SunGraphics2D) g).surfaceData.getDefaultScale();
+                }
+                scaleFactors[0] = scaleFactor;
+                dialog.setVisible(false);
+            }
+        };
+
+        dialog.add(panel);
+        dialog.setVisible(true);
+        dialog.dispose();
+
+        return scaleFactors[0];
+    }
+
+    static void generateImages() throws Exception {
+        for (ImageInfo test : tests) {
+            generateImage(test.name1x, test.color1x, 1);
+            generateImage(test.name2x, test.color2x, 2);
+        }
+    }
+
+    static void generateImage(String name, Color color, int scale) throws Exception {
+        File file = new File(name);
+        if (file.exists()) {
+            return;
+        }
+        BufferedImage image = new BufferedImage(scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT,
+                BufferedImage.TYPE_INT_RGB);
+        Graphics g = image.getGraphics();
+        g.setColor(color);
+        g.fillRect(0, 0, scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT);
+        ImageIO.write(image, "png", file);
+    }
+
+    static class ImageInfo {
+
+        final String name1x;
+        final String name2x;
+        final Color color1x;
+        final Color color2x;
+
+        public ImageInfo(String name1x, String name2x, Color color1x, Color color2x) {
+            this.name1x = name1x;
+            this.name2x = name2x;
+            this.color1x = color1x;
+            this.color2x = color2x;
+        }
+    }
+}