提交 0be06f1c 编写于 作者: A alexsch

8011059: [macosx] Support automatic @2x images loading on Mac OS X

Reviewed-by: serb, flar
上级 a8304d97
...@@ -35,14 +35,17 @@ import java.awt.event.KeyEvent; ...@@ -35,14 +35,17 @@ import java.awt.event.KeyEvent;
import java.awt.im.InputMethodHighlight; import java.awt.im.InputMethodHighlight;
import java.awt.peer.*; import java.awt.peer.*;
import java.lang.reflect.*; import java.lang.reflect.*;
import java.net.URL;
import java.security.*; import java.security.*;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.net.MalformedURLException;
import sun.awt.*; import sun.awt.*;
import sun.lwawt.*; import sun.lwawt.*;
import sun.lwawt.LWWindowPeer.PeerType; import sun.lwawt.LWWindowPeer.PeerType;
import sun.security.action.GetBooleanAction; import sun.security.action.GetBooleanAction;
import sun.awt.image.MultiResolutionImage;
import sun.util.CoreResourceBundleControl; import sun.util.CoreResourceBundleControl;
...@@ -489,9 +492,30 @@ public final class LWCToolkit extends LWToolkit { ...@@ -489,9 +492,30 @@ public final class LWCToolkit extends LWToolkit {
@Override @Override
public Image getImage(final String filename) { public Image getImage(final String filename) {
final Image nsImage = checkForNSImage(filename); final Image nsImage = checkForNSImage(filename);
if (nsImage != null) return nsImage; if (nsImage != null) {
return nsImage;
}
if (imageCached(filename)) {
return super.getImage(filename);
}
return super.getImage(filename); String fileneame2x = getScaledImageName(filename);
return (imageExists(fileneame2x))
? getImageWithResolutionVariant(filename, fileneame2x)
: super.getImage(filename);
}
@Override
public Image getImage(URL url) {
if (imageCached(url)) {
return super.getImage(url);
}
URL url2x = getScaledImageURL(url);
return (imageExists(url2x))
? getImageWithResolutionVariant(url, url2x) : super.getImage(url);
} }
static final String nsImagePrefix = "NSImage://"; static final String nsImagePrefix = "NSImage://";
...@@ -781,4 +805,36 @@ public final class LWCToolkit extends LWToolkit { ...@@ -781,4 +805,36 @@ public final class LWCToolkit extends LWToolkit {
public boolean enableInputMethodsForTextComponent() { public boolean enableInputMethodsForTextComponent() {
return true; return true;
} }
private static URL getScaledImageURL(URL url) {
try {
String scaledImagePath = getScaledImageName(url.getPath());
return scaledImagePath == null ? null : new URL(url.getProtocol(),
url.getHost(), url.getPort(), scaledImagePath);
} catch (MalformedURLException e) {
return null;
}
}
private static String getScaledImageName(String path) {
if (!isValidPath(path)) {
return null;
}
int slash = path.lastIndexOf('/');
String name = (slash < 0) ? path : path.substring(slash + 1);
if (name.contains("@2x")) {
return null;
}
int dot = name.lastIndexOf('.');
String name2x = (dot < 0) ? name + "@2x"
: name.substring(0, dot) + "@2x" + name.substring(dot);
return (slash < 0) ? name2x : path.substring(0, slash + 1) + name2x;
}
private static boolean isValidPath(String path) {
return !path.isEmpty() && !path.endsWith("/") && !path.endsWith(".");
}
} }
...@@ -28,6 +28,7 @@ package java.awt; ...@@ -28,6 +28,7 @@ package java.awt;
import java.awt.Component; import java.awt.Component;
import java.awt.Image; import java.awt.Image;
import java.awt.image.ImageObserver; import java.awt.image.ImageObserver;
import sun.awt.image.MultiResolutionToolkitImage;
/** /**
* The <code>MediaTracker</code> class is a utility class to track * The <code>MediaTracker</code> class is a utility class to track
...@@ -222,10 +223,17 @@ public class MediaTracker implements java.io.Serializable { ...@@ -222,10 +223,17 @@ public class MediaTracker implements java.io.Serializable {
* @param h the height at which the image is rendered * @param h the height at which the image is rendered
*/ */
public synchronized void addImage(Image image, int id, int w, int h) { public synchronized void addImage(Image image, int id, int w, int h) {
addImageImpl(image, id, w, h);
Image rvImage = getResolutionVariant(image);
if (rvImage != null) {
addImageImpl(rvImage, id, 2 * w, 2 * h);
}
}
private void addImageImpl(Image image, int id, int w, int h) {
head = MediaEntry.insert(head, head = MediaEntry.insert(head,
new ImageMediaEntry(this, image, id, w, h)); new ImageMediaEntry(this, image, id, w, h));
} }
/** /**
* Flag indicating that media is currently being loaded. * Flag indicating that media is currently being loaded.
* @see java.awt.MediaTracker#statusAll * @see java.awt.MediaTracker#statusAll
...@@ -719,6 +727,15 @@ public class MediaTracker implements java.io.Serializable { ...@@ -719,6 +727,15 @@ public class MediaTracker implements java.io.Serializable {
* @since JDK1.1 * @since JDK1.1
*/ */
public synchronized void removeImage(Image image) { public synchronized void removeImage(Image image) {
removeImageImpl(image);
Image rvImage = getResolutionVariant(image);
if (rvImage != null) {
removeImageImpl(rvImage);
}
notifyAll(); // Notify in case remaining images are "done".
}
private void removeImageImpl(Image image) {
MediaEntry cur = head; MediaEntry cur = head;
MediaEntry prev = null; MediaEntry prev = null;
while (cur != null) { while (cur != null) {
...@@ -735,7 +752,6 @@ public class MediaTracker implements java.io.Serializable { ...@@ -735,7 +752,6 @@ public class MediaTracker implements java.io.Serializable {
} }
cur = next; cur = next;
} }
notifyAll(); // Notify in case remaining images are "done".
} }
/** /**
...@@ -750,6 +766,15 @@ public class MediaTracker implements java.io.Serializable { ...@@ -750,6 +766,15 @@ public class MediaTracker implements java.io.Serializable {
* @since JDK1.1 * @since JDK1.1
*/ */
public synchronized void removeImage(Image image, int id) { public synchronized void removeImage(Image image, int id) {
removeImageImpl(image, id);
Image rvImage = getResolutionVariant(image);
if (rvImage != null) {
removeImageImpl(rvImage, id);
}
notifyAll(); // Notify in case remaining images are "done".
}
private void removeImageImpl(Image image, int id) {
MediaEntry cur = head; MediaEntry cur = head;
MediaEntry prev = null; MediaEntry prev = null;
while (cur != null) { while (cur != null) {
...@@ -766,7 +791,6 @@ public class MediaTracker implements java.io.Serializable { ...@@ -766,7 +791,6 @@ public class MediaTracker implements java.io.Serializable {
} }
cur = next; cur = next;
} }
notifyAll(); // Notify in case remaining images are "done".
} }
/** /**
...@@ -783,6 +807,16 @@ public class MediaTracker implements java.io.Serializable { ...@@ -783,6 +807,16 @@ public class MediaTracker implements java.io.Serializable {
*/ */
public synchronized void removeImage(Image image, int id, public synchronized void removeImage(Image image, int id,
int width, int height) { int width, int height) {
removeImageImpl(image, id, width, height);
Image rvImage = getResolutionVariant(image);
if (rvImage != null) {
removeImageImpl(rvImage, id, 2 * width, 2 * height);
}
notifyAll(); // Notify in case remaining images are "done".
}
private void removeImageImpl(Image image, int id, int width, int height) {
MediaEntry cur = head; MediaEntry cur = head;
MediaEntry prev = null; MediaEntry prev = null;
while (cur != null) { while (cur != null) {
...@@ -801,12 +835,18 @@ public class MediaTracker implements java.io.Serializable { ...@@ -801,12 +835,18 @@ public class MediaTracker implements java.io.Serializable {
} }
cur = next; cur = next;
} }
notifyAll(); // Notify in case remaining images are "done".
} }
synchronized void setDone() { synchronized void setDone() {
notifyAll(); notifyAll();
} }
private static Image getResolutionVariant(Image image) {
if (image instanceof MultiResolutionToolkitImage) {
return ((MultiResolutionToolkitImage) image).getResolutionVariant();
}
return null;
}
} }
abstract class MediaEntry { abstract class MediaEntry {
......
...@@ -172,7 +172,7 @@ public class SunHints { ...@@ -172,7 +172,7 @@ public class SunHints {
} }
} }
private static final int NUM_KEYS = 9; private static final int NUM_KEYS = 10;
private static final int VALS_PER_KEY = 8; private static final int VALS_PER_KEY = 8;
/** /**
...@@ -252,6 +252,13 @@ public class SunHints { ...@@ -252,6 +252,13 @@ public class SunHints {
@Native public static final int INTVAL_STROKE_NORMALIZE = 1; @Native public static final int INTVAL_STROKE_NORMALIZE = 1;
@Native public static final int INTVAL_STROKE_PURE = 2; @Native public static final int INTVAL_STROKE_PURE = 2;
/**
* Image scaling hint key and values
*/
@Native public static final int INTKEY_RESOLUTION_VARIANT = 9;
@Native public static final int INTVAL_RESOLUTION_VARIANT_DEFAULT = 0;
@Native public static final int INTVAL_RESOLUTION_VARIANT_OFF = 1;
@Native public static final int INTVAL_RESOLUTION_VARIANT_ON = 2;
/** /**
* LCD text contrast control hint key. * LCD text contrast control hint key.
* Value is "100" to make discontiguous with the others which * Value is "100" to make discontiguous with the others which
...@@ -450,6 +457,24 @@ public class SunHints { ...@@ -450,6 +457,24 @@ public class SunHints {
SunHints.INTVAL_STROKE_PURE, SunHints.INTVAL_STROKE_PURE,
"Pure stroke conversion for accurate paths"); "Pure stroke conversion for accurate paths");
/**
* Image resolution variant hint key and value objects
*/
public static final Key KEY_RESOLUTION_VARIANT =
new SunHints.Key(SunHints.INTKEY_RESOLUTION_VARIANT,
"Global image resolution variant key");
public static final Object VALUE_RESOLUTION_VARIANT_DEFAULT =
new SunHints.Value(KEY_RESOLUTION_VARIANT,
SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT,
"Choose image resolutions based on a default heuristic");
public static final Object VALUE_RESOLUTION_VARIANT_OFF =
new SunHints.Value(KEY_RESOLUTION_VARIANT,
SunHints.INTVAL_RESOLUTION_VARIANT_OFF,
"Use only the standard resolution of an image");
public static final Object VALUE_RESOLUTION_VARIANT_ON =
new SunHints.Value(KEY_RESOLUTION_VARIANT,
SunHints.INTVAL_RESOLUTION_VARIANT_ON,
"Always use resolution-specific variants of images");
public static class LCDContrastKey extends Key { public static class LCDContrastKey extends Key {
......
...@@ -36,6 +36,9 @@ import java.awt.image.*; ...@@ -36,6 +36,9 @@ import java.awt.image.*;
import java.awt.TrayIcon; import java.awt.TrayIcon;
import java.awt.SystemTray; import java.awt.SystemTray;
import java.awt.event.InputEvent; import java.awt.event.InputEvent;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
...@@ -715,33 +718,7 @@ public abstract class SunToolkit extends Toolkit ...@@ -715,33 +718,7 @@ public abstract class SunToolkit extends Toolkit
static final SoftCache imgCache = new SoftCache(); static final SoftCache imgCache = new SoftCache();
static Image getImageFromHash(Toolkit tk, URL url) { static Image getImageFromHash(Toolkit tk, URL url) {
SecurityManager sm = System.getSecurityManager(); checkPermissions(url);
if (sm != null) {
try {
java.security.Permission perm =
url.openConnection().getPermission();
if (perm != null) {
try {
sm.checkPermission(perm);
} catch (SecurityException se) {
// fallback to checkRead/checkConnect for pre 1.2
// security managers
if ((perm instanceof java.io.FilePermission) &&
perm.getActions().indexOf("read") != -1) {
sm.checkRead(perm.getName());
} else if ((perm instanceof
java.net.SocketPermission) &&
perm.getActions().indexOf("connect") != -1) {
sm.checkConnect(url.getHost(), url.getPort());
} else {
throw se;
}
}
}
} catch (java.io.IOException ioe) {
sm.checkConnect(url.getHost(), url.getPort());
}
}
synchronized (imgCache) { synchronized (imgCache) {
Image img = (Image)imgCache.get(url); Image img = (Image)imgCache.get(url);
if (img == null) { if (img == null) {
...@@ -757,10 +734,7 @@ public abstract class SunToolkit extends Toolkit ...@@ -757,10 +734,7 @@ public abstract class SunToolkit extends Toolkit
static Image getImageFromHash(Toolkit tk, static Image getImageFromHash(Toolkit tk,
String filename) { String filename) {
SecurityManager security = System.getSecurityManager(); checkPermissions(filename);
if (security != null) {
security.checkRead(filename);
}
synchronized (imgCache) { synchronized (imgCache) {
Image img = (Image)imgCache.get(filename); Image img = (Image)imgCache.get(filename);
if (img == null) { if (img == null) {
...@@ -782,42 +756,42 @@ public abstract class SunToolkit extends Toolkit ...@@ -782,42 +756,42 @@ public abstract class SunToolkit extends Toolkit
return getImageFromHash(this, url); return getImageFromHash(this, url);
} }
public Image createImage(String filename) { protected Image getImageWithResolutionVariant(String fileName,
SecurityManager security = System.getSecurityManager(); String resolutionVariantName) {
if (security != null) { synchronized (imgCache) {
security.checkRead(filename); Image image = getImageFromHash(this, fileName);
if (image instanceof MultiResolutionImage) {
return image;
}
Image resolutionVariant = getImageFromHash(this, resolutionVariantName);
image = createImageWithResolutionVariant(image, resolutionVariant);
imgCache.put(fileName, image);
return image;
} }
return createImage(new FileImageSource(filename));
} }
public Image createImage(URL url) { protected Image getImageWithResolutionVariant(URL url,
SecurityManager sm = System.getSecurityManager(); URL resolutionVariantURL) {
if (sm != null) { synchronized (imgCache) {
try { Image image = getImageFromHash(this, url);
java.security.Permission perm = if (image instanceof MultiResolutionImage) {
url.openConnection().getPermission(); return image;
if (perm != null) {
try {
sm.checkPermission(perm);
} catch (SecurityException se) {
// fallback to checkRead/checkConnect for pre 1.2
// security managers
if ((perm instanceof java.io.FilePermission) &&
perm.getActions().indexOf("read") != -1) {
sm.checkRead(perm.getName());
} else if ((perm instanceof
java.net.SocketPermission) &&
perm.getActions().indexOf("connect") != -1) {
sm.checkConnect(url.getHost(), url.getPort());
} else {
throw se;
}
}
}
} catch (java.io.IOException ioe) {
sm.checkConnect(url.getHost(), url.getPort());
} }
Image resolutionVariant = getImageFromHash(this, resolutionVariantURL);
image = createImageWithResolutionVariant(image, resolutionVariant);
imgCache.put(url, image);
return image;
} }
}
public Image createImage(String filename) {
checkPermissions(filename);
return createImage(new FileImageSource(filename));
}
public Image createImage(URL url) {
checkPermissions(url);
return createImage(new URLImageSource(url)); return createImage(new URLImageSource(url));
} }
...@@ -829,6 +803,11 @@ public abstract class SunToolkit extends Toolkit ...@@ -829,6 +803,11 @@ public abstract class SunToolkit extends Toolkit
return new ToolkitImage(producer); return new ToolkitImage(producer);
} }
public static Image createImageWithResolutionVariant(Image image,
Image resolutionVariant) {
return new MultiResolutionToolkitImage(image, resolutionVariant);
}
public int checkImage(Image img, int w, int h, ImageObserver o) { public int checkImage(Image img, int w, int h, ImageObserver o) {
if (!(img instanceof ToolkitImage)) { if (!(img instanceof ToolkitImage)) {
return ImageObserver.ALLBITS; return ImageObserver.ALLBITS;
...@@ -841,7 +820,7 @@ public abstract class SunToolkit extends Toolkit ...@@ -841,7 +820,7 @@ public abstract class SunToolkit extends Toolkit
} else { } else {
repbits = tkimg.getImageRep().check(o); repbits = tkimg.getImageRep().check(o);
} }
return tkimg.check(o) | repbits; return (tkimg.check(o) | repbits) & checkResolutionVariant(img, w, h, o);
} }
public boolean prepareImage(Image img, int w, int h, ImageObserver o) { public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
...@@ -863,7 +842,97 @@ public abstract class SunToolkit extends Toolkit ...@@ -863,7 +842,97 @@ public abstract class SunToolkit extends Toolkit
return false; return false;
} }
ImageRepresentation ir = tkimg.getImageRep(); ImageRepresentation ir = tkimg.getImageRep();
return ir.prepare(o); return ir.prepare(o) & prepareResolutionVariant(img, w, h, o);
}
private int checkResolutionVariant(Image img, int w, int h, ImageObserver o) {
ToolkitImage rvImage = getResolutionVariant(img);
// Ignore the resolution variant in case of error
return (rvImage == null || rvImage.hasError()) ? 0xFFFF :
checkImage(rvImage, 2 * w, 2 * h, MultiResolutionToolkitImage.
getResolutionVariantObserver(
img, o, w, h, 2 * w, 2 * h));
}
private boolean prepareResolutionVariant(Image img, int w, int h,
ImageObserver o) {
ToolkitImage rvImage = getResolutionVariant(img);
// Ignore the resolution variant in case of error
return rvImage == null || rvImage.hasError() || prepareImage(
rvImage, 2 * w, 2 * h,
MultiResolutionToolkitImage.getResolutionVariantObserver(
img, o, w, h, 2 * w, 2 * h));
}
private static ToolkitImage getResolutionVariant(Image image) {
if (image instanceof MultiResolutionToolkitImage) {
Image resolutionVariant = ((MultiResolutionToolkitImage) image).
getResolutionVariant();
if (resolutionVariant instanceof ToolkitImage) {
return (ToolkitImage) resolutionVariant;
}
}
return null;
}
protected static boolean imageCached(Object key) {
return imgCache.containsKey(key);
}
protected static boolean imageExists(String filename) {
checkPermissions(filename);
return filename != null && new File(filename).exists();
}
@SuppressWarnings("try")
protected static boolean imageExists(URL url) {
checkPermissions(url);
if (url != null) {
try (InputStream is = url.openStream()) {
return true;
}catch(IOException e){
return false;
}
}
return false;
}
private static void checkPermissions(String filename) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(filename);
}
}
private static void checkPermissions(URL url) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
java.security.Permission perm =
url.openConnection().getPermission();
if (perm != null) {
try {
sm.checkPermission(perm);
} catch (SecurityException se) {
// fallback to checkRead/checkConnect for pre 1.2
// security managers
if ((perm instanceof java.io.FilePermission) &&
perm.getActions().indexOf("read") != -1) {
sm.checkRead(perm.getName());
} else if ((perm instanceof
java.net.SocketPermission) &&
perm.getActions().indexOf("connect") != -1) {
sm.checkConnect(url.getHost(), url.getPort());
} else {
throw se;
}
}
}
} catch (java.io.IOException ioe) {
sm.checkConnect(url.getHost(), url.getPort());
}
}
} }
/** /**
......
/*
* Copyright (c) 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.awt.image;
import java.awt.Image;
import java.util.List;
/**
* This interface is designed to provide a set of images at various resolutions.
*
* The <code>MultiResolutionImage</code> interface should be implemented by any
* class whose instances are intended to provide image resolution variants
* according to the given image width and height.
*
* For example,
* <pre>
* {@code
* public class ScaledImage extends BufferedImage
* implements MultiResolutionImage {
*
* @Override
* public Image getResolutionVariant(int width, int height) {
* return ((width <= getWidth() && height <= getHeight()))
* ? this : highResolutionImage;
* }
*
* @Override
* public List<Image> getResolutionVariants() {
* return Arrays.asList(this, highResolutionImage);
* }
* }
* }</pre>
*
* It is recommended to cache image variants for performance reasons.
*
* <b>WARNING</b>: This class is an implementation detail. This API may change
* between update release, and it may even be removed or be moved in some other
* package(s)/class(es).
*/
public interface MultiResolutionImage {
/**
* Provides an image with necessary resolution which best fits to the given
* image width and height.
*
* @param width the desired image resolution width.
* @param height the desired image resolution height.
* @return image resolution variant.
*
* @since JDK1.8
*/
public Image getResolutionVariant(int width, int height);
/**
* Gets list of all resolution variants including the base image
*
* @return list of resolution variants.
* @since JDK1.8
*/
public List<Image> getResolutionVariants();
}
/*
* Copyright (c) 2013, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package sun.awt.image;
import java.awt.Image;
import java.awt.image.ImageObserver;
import java.util.Arrays;
import java.util.List;
import sun.misc.SoftCache;
public class MultiResolutionToolkitImage extends ToolkitImage implements MultiResolutionImage {
Image resolutionVariant;
public MultiResolutionToolkitImage(Image lowResolutionImage, Image resolutionVariant) {
super(lowResolutionImage.getSource());
this.resolutionVariant = resolutionVariant;
}
@Override
public Image getResolutionVariant(int width, int height) {
return ((width <= getWidth() && height <= getHeight()))
? this : resolutionVariant;
}
public Image getResolutionVariant() {
return resolutionVariant;
}
@Override
public List<Image> getResolutionVariants() {
return Arrays.<Image>asList(this, resolutionVariant);
}
private static final int BITS_INFO = ImageObserver.SOMEBITS
| ImageObserver.FRAMEBITS | ImageObserver.ALLBITS;
private static class ObserverCache {
static final SoftCache INSTANCE = new SoftCache();
}
public static ImageObserver getResolutionVariantObserver(
final Image image, final ImageObserver observer,
final int imgWidth, final int imgHeight,
final int rvWidth, final int rvHeight) {
if (observer == null) {
return null;
}
synchronized (ObserverCache.INSTANCE) {
ImageObserver o = (ImageObserver) ObserverCache.INSTANCE.get(image);
if (o == null) {
o = (Image resolutionVariant, int flags,
int x, int y, int width, int height) -> {
if ((flags & (ImageObserver.WIDTH | BITS_INFO)) != 0) {
width = (width + 1) / 2;
}
if ((flags & (ImageObserver.HEIGHT | BITS_INFO)) != 0) {
height = (height + 1) / 2;
}
if ((flags & BITS_INFO) != 0) {
x /= 2;
y /= 2;
}
return observer.imageUpdate(
image, flags, x, y, width, height);
};
ObserverCache.INSTANCE.put(image, o);
}
return o;
}
}
}
...@@ -61,6 +61,7 @@ import java.awt.FontMetrics; ...@@ -61,6 +61,7 @@ import java.awt.FontMetrics;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.text.AttributedCharacterIterator; import java.text.AttributedCharacterIterator;
import java.awt.Font; import java.awt.Font;
import java.awt.Point;
import java.awt.image.ImageObserver; import java.awt.image.ImageObserver;
import java.awt.Transparency; import java.awt.Transparency;
import java.awt.font.GlyphVector; import java.awt.font.GlyphVector;
...@@ -93,6 +94,13 @@ import java.util.Iterator; ...@@ -93,6 +94,13 @@ import java.util.Iterator;
import sun.misc.PerformanceLogger; import sun.misc.PerformanceLogger;
import java.lang.annotation.Native; import java.lang.annotation.Native;
import sun.awt.image.MultiResolutionImage;
import static java.awt.geom.AffineTransform.TYPE_FLIP;
import static java.awt.geom.AffineTransform.TYPE_MASK_SCALE;
import static java.awt.geom.AffineTransform.TYPE_TRANSLATION;
import sun.awt.image.MultiResolutionToolkitImage;
import sun.awt.image.ToolkitImage;
/** /**
* This is a the master Graphics2D superclass for all of the Sun * This is a the master Graphics2D superclass for all of the Sun
...@@ -237,6 +245,7 @@ public final class SunGraphics2D ...@@ -237,6 +245,7 @@ public final class SunGraphics2D
protected Region devClip; // Actual physical drawable in pixels protected Region devClip; // Actual physical drawable in pixels
private final int devScale; // Actual physical scale factor private final int devScale; // Actual physical scale factor
private int resolutionVariantHint;
// cached state for text rendering // cached state for text rendering
private boolean validFontInfo; private boolean validFontInfo;
...@@ -274,6 +283,7 @@ public final class SunGraphics2D ...@@ -274,6 +283,7 @@ public final class SunGraphics2D
lcdTextContrast = lcdTextContrastDefaultValue; lcdTextContrast = lcdTextContrastDefaultValue;
interpolationHint = -1; interpolationHint = -1;
strokeHint = SunHints.INTVAL_STROKE_DEFAULT; strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
resolutionVariantHint = SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT;
interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR; interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
...@@ -1249,6 +1259,10 @@ public final class SunGraphics2D ...@@ -1249,6 +1259,10 @@ public final class SunGraphics2D
stateChanged = (strokeHint != newHint); stateChanged = (strokeHint != newHint);
strokeHint = newHint; strokeHint = newHint;
break; break;
case SunHints.INTKEY_RESOLUTION_VARIANT:
stateChanged = (resolutionVariantHint != newHint);
resolutionVariantHint = newHint;
break;
default: default:
recognized = false; recognized = false;
stateChanged = false; stateChanged = false;
...@@ -1322,6 +1336,9 @@ public final class SunGraphics2D ...@@ -1322,6 +1336,9 @@ public final class SunGraphics2D
case SunHints.INTKEY_STROKE_CONTROL: case SunHints.INTKEY_STROKE_CONTROL:
return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL, return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
strokeHint); strokeHint);
case SunHints.INTKEY_RESOLUTION_VARIANT:
return SunHints.Value.get(SunHints.INTKEY_RESOLUTION_VARIANT,
resolutionVariantHint);
} }
return null; return null;
} }
...@@ -3050,18 +3067,58 @@ public final class SunGraphics2D ...@@ -3050,18 +3067,58 @@ public final class SunGraphics2D
} }
// end of text rendering methods // end of text rendering methods
private static boolean isHiDPIImage(final Image img) { private boolean isHiDPIImage(final Image img) {
return SurfaceManager.getImageScale(img) != 1; return (SurfaceManager.getImageScale(img) != 1) ||
(resolutionVariantHint != SunHints.INTVAL_RESOLUTION_VARIANT_OFF
&& img instanceof MultiResolutionImage);
} }
private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2, private boolean drawHiDPIImage(Image img, int dx1, int dy1, int dx2,
int dy2, int sx1, int sy1, int sx2, int sy2, int dy2, int sx1, int sy1, int sx2, int sy2,
Color bgcolor, ImageObserver observer) { Color bgcolor, ImageObserver observer) {
final int scale = SurfaceManager.getImageScale(img);
sx1 = Region.clipScale(sx1, scale); if (SurfaceManager.getImageScale(img) != 1) { // Volatile Image
sx2 = Region.clipScale(sx2, scale); final int scale = SurfaceManager.getImageScale(img);
sy1 = Region.clipScale(sy1, scale); sx1 = Region.clipScale(sx1, scale);
sy2 = Region.clipScale(sy2, scale); sx2 = Region.clipScale(sx2, scale);
sy1 = Region.clipScale(sy1, scale);
sy2 = Region.clipScale(sy2, scale);
} else if (img instanceof MultiResolutionImage) {
// get scaled destination image size
int width = img.getWidth(observer);
int height = img.getHeight(observer);
Image resolutionVariant = getResolutionVariant(
(MultiResolutionImage) img, width, height,
dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2);
if (resolutionVariant != img && resolutionVariant != null) {
// recalculate source region for the resolution variant
ImageObserver rvObserver = MultiResolutionToolkitImage.
getResolutionVariantObserver(img, observer,
width, height, -1, -1);
int rvWidth = resolutionVariant.getWidth(rvObserver);
int rvHeight = resolutionVariant.getHeight(rvObserver);
if (0 < width && 0 < height && 0 < rvWidth && 0 < rvHeight) {
float widthScale = ((float) rvWidth) / width;
float heightScale = ((float) rvHeight) / height;
sx1 = Region.clipScale(sx1, widthScale);
sy1 = Region.clipScale(sy1, heightScale);
sx2 = Region.clipScale(sx2, widthScale);
sy2 = Region.clipScale(sy2, heightScale);
observer = rvObserver;
img = resolutionVariant;
}
}
}
try { try {
return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1, return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
sx2, sy2, bgcolor, observer); sx2, sy2, bgcolor, observer);
...@@ -3081,6 +3138,54 @@ public final class SunGraphics2D ...@@ -3081,6 +3138,54 @@ public final class SunGraphics2D
} }
} }
private Image getResolutionVariant(MultiResolutionImage img,
int srcWidth, int srcHeight, int dx1, int dy1, int dx2, int dy2,
int sx1, int sy1, int sx2, int sy2) {
if (srcWidth <= 0 || srcHeight <= 0) {
return null;
}
int sw = sx2 - sx1;
int sh = sy2 - sy1;
if (sw == 0 || sh == 0) {
return null;
}
int type = transform.getType();
int dw = dx2 - dx1;
int dh = dy2 - dy1;
double destRegionWidth;
double destRegionHeight;
if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP)) == 0) {
destRegionWidth = dw;
destRegionHeight = dh;
} else if ((type & ~(TYPE_TRANSLATION | TYPE_FLIP | TYPE_MASK_SCALE)) == 0) {
destRegionWidth = dw * transform.getScaleX();
destRegionHeight = dh * transform.getScaleY();
} else {
destRegionWidth = dw * Math.hypot(
transform.getScaleX(), transform.getShearY());
destRegionHeight = dh * Math.hypot(
transform.getShearX(), transform.getScaleY());
}
int destImageWidth = (int) Math.abs(srcWidth * destRegionWidth / sw);
int destImageHeight = (int) Math.abs(srcHeight * destRegionHeight / sh);
Image resolutionVariant
= img.getResolutionVariant(destImageWidth, destImageHeight);
if (resolutionVariant instanceof ToolkitImage
&& ((ToolkitImage) resolutionVariant).hasError()) {
return null;
}
return resolutionVariant;
}
/** /**
* Draws an image scaled to x,y,w,h in nonblocking mode with a * Draws an image scaled to x,y,w,h in nonblocking mode with a
* callback object. * callback object.
......
/*
* Copyright (c) 2013, 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.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import javax.imageio.ImageIO;
import sun.awt.OSInfo;
import sun.awt.SunHints;
import java.awt.MediaTracker;
import java.awt.geom.AffineTransform;
import java.awt.image.ImageObserver;
import java.util.Arrays;
import java.util.List;
import javax.swing.JPanel;
import sun.awt.SunToolkit;
import sun.awt.image.MultiResolutionImage;
/**
* @test
* @bug 8011059
* @author Alexander Scherbatiy
* @summary [macosx] Make JDK demos look perfect on retina displays
* @run main MultiResolutionImageTest CUSTOM
* @run main MultiResolutionImageTest TOOLKIT_PREPARE
* @run main MultiResolutionImageTest TOOLKIT_LOAD
* @run main MultiResolutionImageTest TOOLKIT
*/
public class MultiResolutionImageTest {
private static final int IMAGE_WIDTH = 300;
private static final int IMAGE_HEIGHT = 200;
private static final Color COLOR_1X = Color.GREEN;
private static final Color COLOR_2X = Color.BLUE;
private static final String IMAGE_NAME_1X = "image.png";
private static final String IMAGE_NAME_2X = "image@2x.png";
public static void main(String[] args) throws Exception {
System.out.println("args: " + args.length);
if (args.length == 0) {
throw new RuntimeException("Not found a test");
}
String test = args[0];
System.out.println("TEST: " + test);
System.out.println("CHECK OS: " + checkOS());
if ("CUSTOM".equals(test)) {
testCustomMultiResolutionImage();
} else if (checkOS()) {
switch (test) {
case "CUSTOM":
break;
case "TOOLKIT_PREPARE":
testToolkitMultiResolutionImagePrepare();
break;
case "TOOLKIT_LOAD":
testToolkitMultiResolutionImageLoad();
break;
case "TOOLKIT":
testToolkitMultiResolutionImage();
testImageNameTo2xParsing();
break;
default:
throw new RuntimeException("Unknown test: " + test);
}
}
}
static boolean checkOS() {
return OSInfo.getOSType() == OSInfo.OSType.MACOSX;
}
public static void testCustomMultiResolutionImage() {
testCustomMultiResolutionImage(false);
testCustomMultiResolutionImage(true);
}
public static void testCustomMultiResolutionImage(boolean enableImageScaling) {
Image image = new MultiResolutionBufferedImage();
// Same image size
BufferedImage bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.drawImage(image, 0, 0, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
// Twice image size
bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT,
BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
// Scale 2x
bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.scale(2, 2);
g2d.drawImage(image, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
// Rotate
bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.drawImage(image, 0, 0, null);
g2d.rotate(Math.PI / 4);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
// Scale 2x and Rotate
bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.scale(-2, 2);
g2d.rotate(-Math.PI / 10);
g2d.drawImage(image, -IMAGE_WIDTH, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
// General Transform
bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
float delta = 0.05f;
float cos = 1 - delta * delta / 2;
float sin = 1 + delta;
AffineTransform transform = new AffineTransform(2 * cos, 0.1, 0.3, -2 * sin, 10, -5);
g2d.setTransform(transform);
g2d.drawImage(image, 0, -IMAGE_HEIGHT, IMAGE_WIDTH, IMAGE_HEIGHT, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
int D = 10;
// From Source to small Destination region
bufferedImage = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.drawImage(image, IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D,
D, D, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
// From Source to large Destination region
bufferedImage = new BufferedImage(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) bufferedImage.getGraphics();
setImageScalingHint(g2d, enableImageScaling);
g2d.drawImage(image, D, D, 2 * IMAGE_WIDTH - D, 2 * IMAGE_HEIGHT - D,
IMAGE_WIDTH / 2, IMAGE_HEIGHT / 2, IMAGE_WIDTH - D, IMAGE_HEIGHT - D, null);
checkColor(bufferedImage.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
}
static class MultiResolutionBufferedImage extends BufferedImage
implements MultiResolutionImage {
Image highResolutionImage;
public MultiResolutionBufferedImage() {
super(IMAGE_WIDTH, IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
highResolutionImage = new BufferedImage(
2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
draw(getGraphics(), 1);
draw(highResolutionImage.getGraphics(), 2);
}
void draw(Graphics graphics, float resolution) {
Graphics2D g2 = (Graphics2D) graphics;
g2.scale(resolution, resolution);
g2.setColor((resolution == 1) ? COLOR_1X : COLOR_2X);
g2.fillRect(0, 0, IMAGE_WIDTH, IMAGE_HEIGHT);
}
@Override
public Image getResolutionVariant(int width, int height) {
return ((width <= getWidth() && height <= getHeight()))
? this : highResolutionImage;
}
@Override
public List<Image> getResolutionVariants() {
return Arrays.asList(this, highResolutionImage);
}
}
static void testToolkitMultiResolutionImagePrepare() throws Exception {
generateImages();
File imageFile = new File(IMAGE_NAME_1X);
String fileName = imageFile.getAbsolutePath();
Image image = Toolkit.getDefaultToolkit().getImage(fileName);
SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
toolkit.prepareImage(image, IMAGE_WIDTH, IMAGE_HEIGHT, new LoadImageObserver(image));
testToolkitMultiResolutionImageLoad(image);
}
static void testToolkitMultiResolutionImageLoad() throws Exception {
generateImages();
File imageFile = new File(IMAGE_NAME_1X);
String fileName = imageFile.getAbsolutePath();
Image image = Toolkit.getDefaultToolkit().getImage(fileName);
testToolkitMultiResolutionImageLoad(image);
}
static void testToolkitMultiResolutionImageLoad(Image image) throws Exception {
MediaTracker tracker = new MediaTracker(new JPanel());
tracker.addImage(image, 0);
tracker.waitForID(0);
if (tracker.isErrorAny()) {
throw new RuntimeException("Error during image loading");
}
tracker.removeImage(image, 0);
testImageLoaded(image);
int w = image.getWidth(null);
int h = image.getHeight(null);
Image resolutionVariant = ((MultiResolutionImage) image)
.getResolutionVariant(2 * w, 2 * h);
if (image == resolutionVariant) {
throw new RuntimeException("Resolution variant is not loaded");
}
testImageLoaded(resolutionVariant);
}
static void testImageLoaded(Image image) {
SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
int flags = toolkit.checkImage(image, IMAGE_WIDTH, IMAGE_WIDTH, new SilentImageObserver());
if ((flags & (ImageObserver.FRAMEBITS | ImageObserver.ALLBITS)) == 0) {
throw new RuntimeException("Image is not loaded!");
}
}
static class SilentImageObserver implements ImageObserver {
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
throw new RuntimeException("Observer should not be called!");
}
}
static class LoadImageObserver implements ImageObserver {
Image image;
public LoadImageObserver(Image image) {
this.image = image;
}
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
if (image != img) {
throw new RuntimeException("Original image is not passed to the observer");
}
if ((infoflags & ImageObserver.WIDTH) != 0) {
if (width != IMAGE_WIDTH) {
throw new RuntimeException("Original width is not passed to the observer");
}
}
if ((infoflags & ImageObserver.HEIGHT) != 0) {
if (height != IMAGE_HEIGHT) {
throw new RuntimeException("Original height is not passed to the observer");
}
}
return (infoflags & ALLBITS) == 0;
}
}
static void testToolkitMultiResolutionImage() throws Exception {
generateImages();
File imageFile = new File(IMAGE_NAME_1X);
String fileName = imageFile.getAbsolutePath();
URL url = imageFile.toURI().toURL();
testToolkitMultiResolutionImageChache(fileName, url);
Image image = Toolkit.getDefaultToolkit().getImage(fileName);
testToolkitImageObserver(image);
testToolkitMultiResolutionImage(image, false);
testToolkitMultiResolutionImage(image, true);
image = Toolkit.getDefaultToolkit().getImage(url);
testToolkitImageObserver(image);
testToolkitMultiResolutionImage(image, false);
testToolkitMultiResolutionImage(image, true);
}
static void testToolkitMultiResolutionImageChache(String fileName, URL url) {
Image img1 = Toolkit.getDefaultToolkit().getImage(fileName);
if (!(img1 instanceof MultiResolutionImage)) {
throw new RuntimeException("Not a MultiResolutionImage");
}
Image img2 = Toolkit.getDefaultToolkit().getImage(fileName);
if (img1 != img2) {
throw new RuntimeException("Image is not cached");
}
img1 = Toolkit.getDefaultToolkit().getImage(url);
if (!(img1 instanceof MultiResolutionImage)) {
throw new RuntimeException("Not a MultiResolutionImage");
}
img2 = Toolkit.getDefaultToolkit().getImage(url);
if (img1 != img2) {
throw new RuntimeException("Image is not cached");
}
}
static void testToolkitMultiResolutionImage(Image image, boolean enableImageScaling)
throws Exception {
MediaTracker tracker = new MediaTracker(new JPanel());
tracker.addImage(image, 0);
tracker.waitForID(0);
if (tracker.isErrorAny()) {
throw new RuntimeException("Error during image loading");
}
final BufferedImage bufferedImage1x = new BufferedImage(IMAGE_WIDTH, IMAGE_HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics2D g1x = (Graphics2D) bufferedImage1x.getGraphics();
setImageScalingHint(g1x, false);
g1x.drawImage(image, 0, 0, null);
checkColor(bufferedImage1x.getRGB(3 * IMAGE_WIDTH / 4, 3 * IMAGE_HEIGHT / 4), false);
Image resolutionVariant = ((MultiResolutionImage) image).
getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT);
if (resolutionVariant == null) {
throw new RuntimeException("Resolution variant is null");
}
MediaTracker tracker2x = new MediaTracker(new JPanel());
tracker2x.addImage(resolutionVariant, 0);
tracker2x.waitForID(0);
if (tracker2x.isErrorAny()) {
throw new RuntimeException("Error during scalable image loading");
}
final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH,
2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics();
setImageScalingHint(g2x, enableImageScaling);
g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
checkColor(bufferedImage2x.getRGB(3 * IMAGE_WIDTH / 2, 3 * IMAGE_HEIGHT / 2), enableImageScaling);
if (!(image instanceof MultiResolutionImage)) {
throw new RuntimeException("Not a MultiResolutionImage");
}
MultiResolutionImage multiResolutionImage = (MultiResolutionImage) image;
Image image1x = multiResolutionImage.getResolutionVariant(IMAGE_WIDTH, IMAGE_HEIGHT);
Image image2x = multiResolutionImage.getResolutionVariant(2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT);
if (image1x.getWidth(null) * 2 != image2x.getWidth(null)
|| image1x.getHeight(null) * 2 != image2x.getHeight(null)) {
throw new RuntimeException("Wrong resolution variant size");
}
}
static void testToolkitImageObserver(final Image image) {
ImageObserver observer = new ImageObserver() {
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
if (img != image) {
throw new RuntimeException("Wrong image in observer");
}
if ((infoflags & (ImageObserver.ERROR | ImageObserver.ABORT)) != 0) {
throw new RuntimeException("Error during image loading");
}
return (infoflags & ImageObserver.ALLBITS) == 0;
}
};
final BufferedImage bufferedImage2x = new BufferedImage(2 * IMAGE_WIDTH,
2 * IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g2x = (Graphics2D) bufferedImage2x.getGraphics();
setImageScalingHint(g2x, true);
g2x.drawImage(image, 0, 0, 2 * IMAGE_WIDTH, 2 * IMAGE_HEIGHT, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, observer);
}
static void setImageScalingHint(Graphics2D g2d, boolean enableImageScaling) {
g2d.setRenderingHint(SunHints.KEY_RESOLUTION_VARIANT, enableImageScaling
? SunHints.VALUE_RESOLUTION_VARIANT_ON
: SunHints.VALUE_RESOLUTION_VARIANT_OFF);
}
static void checkColor(int rgb, boolean isImageScaled) {
if (!isImageScaled && COLOR_1X.getRGB() != rgb) {
throw new RuntimeException("Wrong 1x color: " + new Color(rgb));
}
if (isImageScaled && COLOR_2X.getRGB() != rgb) {
throw new RuntimeException("Wrong 2x color" + new Color(rgb));
}
}
static void generateImages() throws Exception {
if (!new File(IMAGE_NAME_1X).exists()) {
generateImage(1);
}
if (!new File(IMAGE_NAME_2X).exists()) {
generateImage(2);
}
}
static void generateImage(int scale) throws Exception {
BufferedImage image = new BufferedImage(scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(scale == 1 ? COLOR_1X : COLOR_2X);
g.fillRect(0, 0, scale * IMAGE_WIDTH, scale * IMAGE_HEIGHT);
File file = new File(scale == 1 ? IMAGE_NAME_1X : IMAGE_NAME_2X);
ImageIO.write(image, "png", file);
}
static void testImageNameTo2xParsing() throws Exception {
for (String[] testNames : TEST_FILE_NAMES) {
String testName = testNames[0];
String goldenName = testNames[1];
String resultName = getTestScaledImageName(testName);
if (!isValidPath(testName) && resultName == null) {
continue;
}
if (goldenName.equals(resultName)) {
continue;
}
throw new RuntimeException("Test name " + testName
+ ", result name: " + resultName);
}
for (URL[] testURLs : TEST_URLS) {
URL testURL = testURLs[0];
URL goldenURL = testURLs[1];
URL resultURL = getTestScaledImageURL(testURL);
if (!isValidPath(testURL.getPath()) && resultURL == null) {
continue;
}
if (goldenURL.equals(resultURL)) {
continue;
}
throw new RuntimeException("Test url: " + testURL
+ ", result url: " + resultURL);
}
}
static URL getTestScaledImageURL(URL url) throws Exception {
Method method = getScalableImageMethod("getScaledImageURL", URL.class);
return (URL) method.invoke(null, url);
}
static String getTestScaledImageName(String name) throws Exception {
Method method = getScalableImageMethod("getScaledImageName", String.class);
return (String) method.invoke(null, name);
}
private static boolean isValidPath(String path) {
return !path.isEmpty() && !path.endsWith("/") && !path.endsWith(".")
&& !path.contains("@2x");
}
private static Method getScalableImageMethod(String name,
Class... parameterTypes) throws Exception {
Toolkit toolkit = Toolkit.getDefaultToolkit();
Method method = toolkit.getClass().getDeclaredMethod(name, parameterTypes);
method.setAccessible(true);
return method;
}
private static final String[][] TEST_FILE_NAMES;
private static final URL[][] TEST_URLS;
static {
TEST_FILE_NAMES = new String[][]{
{"", null},
{".", null},
{"..", null},
{"/", null},
{"/.", null},
{"dir/", null},
{"dir/.", null},
{"aaa@2x.png", null},
{"/dir/aaa@2x.png", null},
{"image", "image@2x"},
{"image.ext", "image@2x.ext"},
{"image.aaa.ext", "image.aaa@2x.ext"},
{"dir/image", "dir/image@2x"},
{"dir/image.ext", "dir/image@2x.ext"},
{"dir/image.aaa.ext", "dir/image.aaa@2x.ext"},
{"dir/aaa.bbb/image", "dir/aaa.bbb/image@2x"},
{"dir/aaa.bbb/image.ext", "dir/aaa.bbb/image@2x.ext"},
{"dir/aaa.bbb/image.ccc.ext", "dir/aaa.bbb/image.ccc@2x.ext"},
{"/dir/image", "/dir/image@2x"},
{"/dir/image.ext", "/dir/image@2x.ext"},
{"/dir/image.aaa.ext", "/dir/image.aaa@2x.ext"},
{"/dir/aaa.bbb/image", "/dir/aaa.bbb/image@2x"},
{"/dir/aaa.bbb/image.ext", "/dir/aaa.bbb/image@2x.ext"},
{"/dir/aaa.bbb/image.ccc.ext", "/dir/aaa.bbb/image.ccc@2x.ext"}
};
try {
TEST_URLS = new URL[][]{
// file
{new URL("file:/aaa"), new URL("file:/aaa@2x")},
{new URL("file:/aaa.ext"), new URL("file:/aaa@2x.ext")},
{new URL("file:/aaa.bbb.ext"), new URL("file:/aaa.bbb@2x.ext")},
{new URL("file:/ccc/aaa.bbb.ext"),
new URL("file:/ccc/aaa.bbb@2x.ext")},
{new URL("file:/ccc.ddd/aaa.bbb.ext"),
new URL("file:/ccc.ddd/aaa.bbb@2x.ext")},
{new URL("file:///~/image"), new URL("file:///~/image@2x")},
{new URL("file:///~/image.ext"),
new URL("file:///~/image@2x.ext")},
// http
{new URL("http://www.test.com"), null},
{new URL("http://www.test.com/"), null},
{new URL("http://www.test.com///"), null},
{new URL("http://www.test.com/image"),
new URL("http://www.test.com/image@2x")},
{new URL("http://www.test.com/image.ext"),
new URL("http://www.test.com/image@2x.ext")},
{new URL("http://www.test.com/dir/image"),
new URL("http://www.test.com/dir/image@2x")},
{new URL("http://www.test.com:80/dir/image.aaa.ext"),
new URL("http://www.test.com:80/dir/image.aaa@2x.ext")},
{new URL("http://www.test.com:8080/dir/image.aaa.ext"),
new URL("http://www.test.com:8080/dir/image.aaa@2x.ext")},
// jar
{new URL("jar:file:/dir/Java2D.jar!/image"),
new URL("jar:file:/dir/Java2D.jar!/image@2x")},
{new URL("jar:file:/dir/Java2D.jar!/image.aaa.ext"),
new URL("jar:file:/dir/Java2D.jar!/image.aaa@2x.ext")},
{new URL("jar:file:/dir/Java2D.jar!/images/image"),
new URL("jar:file:/dir/Java2D.jar!/images/image@2x")},
{new URL("jar:file:/dir/Java2D.jar!/images/image.ext"),
new URL("jar:file:/dir/Java2D.jar!/images/image@2x.ext")},
{new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image.ext"),
new URL("jar:file:/aaa.bbb/Java2D.jar!/images/image@2x.ext")},
{new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image.ext"),
new URL("jar:file:/dir/Java2D.jar!/aaa.bbb/image@2x.ext")},};
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static class PreloadedImageObserver implements ImageObserver {
@Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
throw new RuntimeException("Image should be already preloaded");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册