--- old/src/share/bin/java.c 2014-06-23 13:44:22.000000000 +0400 +++ new/src/share/bin/java.c 2014-06-23 13:44:22.000000000 +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) { - DoSplashInit(); - DoSplashLoadFile(file_name); } else { - return; + DoSplashInit(); + 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); /* --- old/src/share/bin/splashscreen.h 2014-06-23 13:44:23.000000000 +0400 +++ new/src/share/bin/splashscreen.h 2014-06-23 13:44:23.000000000 +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); --- old/src/share/bin/splashscreen_stubs.c 2014-06-23 13:44:24.000000000 +0400 +++ new/src/share/bin/splashscreen_stubs.c 2014-06-23 13:44:23.000000000 +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 --- old/src/share/native/sun/awt/splashscreen/splashscreen_impl.h 2014-06-23 13:44:24.000000000 +0400 +++ new/src/share/native/sun/awt/splashscreen/splashscreen_impl.h 2014-06-23 13:44:24.000000000 +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 { --- old/src/share/native/sun/awt/splashscreen/splashscreen_impl.c 2014-06-23 13:44:25.000000000 +0400 +++ new/src/share/native/sun/awt/splashscreen/splashscreen_impl.c 2014-06-23 13:44:25.000000000 +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) { @@ -365,7 +373,7 @@ static void closeMem(void* pStream) { } -int SplashStreamInitFile(SplashStream * pStream, const char* filename) { +int SplashStreamInitFile(SplashStream * pStream, const char* filename) { pStream->arg.stdio.f = fopen(filename, "rb"); pStream->read = readFile; pStream->peek = peekFile; --- old/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c 2014-06-23 13:44:26.000000000 +0400 +++ new/src/windows/native/sun/awt/splashscreen/splashscreen_sys.c 2014-06-23 13:44:26.000000000 +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; +} --- old/src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c 2014-06-23 13:44:27.000000000 +0400 +++ new/src/solaris/native/sun/awt/splashscreen/splashscreen_sys.c 2014-06-23 13:44:27.000000000 +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; +} --- old/src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m 2014-06-23 13:44:28.000000000 +0400 +++ new/src/macosx/native/sun/awt/splashscreen/splashscreen_sys.m 2014-06-23 13:44:27.000000000 +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 (0 < scaleFactor && 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]; --- old/src/share/native/sun/awt/splashscreen/java_awt_SplashScreen.c 2014-06-23 13:44:28.000000000 +0400 +++ new/src/share/native/sun/awt/splashscreen/java_awt_SplashScreen.c 2014-06-23 13:44:28.000000000 +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 --- old/make/mapfiles/libsplashscreen/mapfile-vers 2014-06-23 13:44:29.000000000 +0400 +++ new/make/mapfiles/libsplashscreen/mapfile-vers 2014-06-23 13:44:29.000000000 +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; --- old/src/share/classes/java/awt/SplashScreen.java 2014-06-23 13:44:30.000000000 +0400 +++ new/src/share/classes/java/awt/SplashScreen.java 2014-06-23 13:44:30.000000000 +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 0 < scale; + if (0 < scale && 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); + } + float scale = _getScaleFactor(splashPtr); + Graphics2D g = image.createGraphics(); + assert (0 < scale); + if (0 < scale) { + scale = 1; } - return image.createGraphics(); + 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); }; --- /dev/null 2014-06-23 13:44:31.000000000 +0400 +++ new/test/java/awt/SplashScreen/MultiResolutionSplash/MultiResolutionSplashTest.java 2014-06-23 13:44:30.000000000 +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; + } + } +}