8043869: [macosx] java -splash does not honor @2x hi dpi notation for retina support
Reviewed-by: pchelko, anthony, ksrini
--- 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;
+ }
+ }
+}