8011059: [macosx] Support automatic @2x images loading on Mac OS X
Reviewed-by: serb, flar
This commit is contained in:
parent
4d23e24741
commit
7e1716cfe0
@ -35,14 +35,17 @@ import java.awt.event.KeyEvent;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.awt.peer.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.net.URL;
|
||||
import java.security.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import sun.awt.*;
|
||||
import sun.lwawt.*;
|
||||
import sun.lwawt.LWWindowPeer.PeerType;
|
||||
import sun.security.action.GetBooleanAction;
|
||||
import sun.awt.image.MultiResolutionImage;
|
||||
|
||||
import sun.util.CoreResourceBundleControl;
|
||||
|
||||
@ -489,9 +492,30 @@ public final class LWCToolkit extends LWToolkit {
|
||||
@Override
|
||||
public Image getImage(final String filename) {
|
||||
final Image nsImage = checkForNSImage(filename);
|
||||
if (nsImage != null) return nsImage;
|
||||
if (nsImage != null) {
|
||||
return nsImage;
|
||||
}
|
||||
|
||||
return super.getImage(filename);
|
||||
if (imageCached(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://";
|
||||
@ -781,4 +805,36 @@ public final class LWCToolkit extends LWToolkit {
|
||||
public boolean enableInputMethodsForTextComponent() {
|
||||
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;
|
||||
import java.awt.Component;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.ImageObserver;
|
||||
import sun.awt.image.MultiResolutionToolkitImage;
|
||||
|
||||
/**
|
||||
* The <code>MediaTracker</code> class is a utility class to track
|
||||
@ -222,10 +223,17 @@ public class MediaTracker implements java.io.Serializable {
|
||||
* @param h the height at which the image is rendered
|
||||
*/
|
||||
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,
|
||||
new ImageMediaEntry(this, image, id, w, h));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flag indicating that media is currently being loaded.
|
||||
* @see java.awt.MediaTracker#statusAll
|
||||
@ -719,6 +727,15 @@ public class MediaTracker implements java.io.Serializable {
|
||||
* @since JDK1.1
|
||||
*/
|
||||
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 prev = null;
|
||||
while (cur != null) {
|
||||
@ -735,7 +752,6 @@ public class MediaTracker implements java.io.Serializable {
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
notifyAll(); // Notify in case remaining images are "done".
|
||||
}
|
||||
|
||||
/**
|
||||
@ -750,6 +766,15 @@ public class MediaTracker implements java.io.Serializable {
|
||||
* @since JDK1.1
|
||||
*/
|
||||
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 prev = null;
|
||||
while (cur != null) {
|
||||
@ -766,7 +791,6 @@ public class MediaTracker implements java.io.Serializable {
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
notifyAll(); // Notify in case remaining images are "done".
|
||||
}
|
||||
|
||||
/**
|
||||
@ -783,6 +807,16 @@ public class MediaTracker implements java.io.Serializable {
|
||||
*/
|
||||
public synchronized void removeImage(Image image, int id,
|
||||
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 prev = null;
|
||||
while (cur != null) {
|
||||
@ -801,12 +835,18 @@ public class MediaTracker implements java.io.Serializable {
|
||||
}
|
||||
cur = next;
|
||||
}
|
||||
notifyAll(); // Notify in case remaining images are "done".
|
||||
}
|
||||
|
||||
synchronized void setDone() {
|
||||
notifyAll();
|
||||
}
|
||||
|
||||
private static Image getResolutionVariant(Image image) {
|
||||
if (image instanceof MultiResolutionToolkitImage) {
|
||||
return ((MultiResolutionToolkitImage) image).getResolutionVariant();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MediaEntry {
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
@ -252,6 +252,13 @@ public class SunHints {
|
||||
@Native public static final int INTVAL_STROKE_NORMALIZE = 1;
|
||||
@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.
|
||||
* Value is "100" to make discontiguous with the others which
|
||||
@ -450,6 +457,24 @@ public class SunHints {
|
||||
SunHints.INTVAL_STROKE_PURE,
|
||||
"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 {
|
||||
|
||||
|
@ -36,6 +36,9 @@ import java.awt.image.*;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -715,33 +718,7 @@ public abstract class SunToolkit extends Toolkit
|
||||
static final SoftCache imgCache = new SoftCache();
|
||||
|
||||
static Image getImageFromHash(Toolkit tk, 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());
|
||||
}
|
||||
}
|
||||
checkPermissions(url);
|
||||
synchronized (imgCache) {
|
||||
Image img = (Image)imgCache.get(url);
|
||||
if (img == null) {
|
||||
@ -757,10 +734,7 @@ public abstract class SunToolkit extends Toolkit
|
||||
|
||||
static Image getImageFromHash(Toolkit tk,
|
||||
String filename) {
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkRead(filename);
|
||||
}
|
||||
checkPermissions(filename);
|
||||
synchronized (imgCache) {
|
||||
Image img = (Image)imgCache.get(filename);
|
||||
if (img == null) {
|
||||
@ -782,15 +756,156 @@ public abstract class SunToolkit extends Toolkit
|
||||
return getImageFromHash(this, url);
|
||||
}
|
||||
|
||||
public Image createImage(String filename) {
|
||||
SecurityManager security = System.getSecurityManager();
|
||||
if (security != null) {
|
||||
security.checkRead(filename);
|
||||
protected Image getImageWithResolutionVariant(String fileName,
|
||||
String resolutionVariantName) {
|
||||
synchronized (imgCache) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected Image getImageWithResolutionVariant(URL url,
|
||||
URL resolutionVariantURL) {
|
||||
synchronized (imgCache) {
|
||||
Image image = getImageFromHash(this, url);
|
||||
if (image instanceof MultiResolutionImage) {
|
||||
return image;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
public Image createImage(byte[] data, int offset, int length) {
|
||||
return createImage(new ByteArrayImageSource(data, offset, length));
|
||||
}
|
||||
|
||||
public Image createImage(ImageProducer 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) {
|
||||
if (!(img instanceof ToolkitImage)) {
|
||||
return ImageObserver.ALLBITS;
|
||||
}
|
||||
|
||||
ToolkitImage tkimg = (ToolkitImage)img;
|
||||
int repbits;
|
||||
if (w == 0 || h == 0) {
|
||||
repbits = ImageObserver.ALLBITS;
|
||||
} else {
|
||||
repbits = tkimg.getImageRep().check(o);
|
||||
}
|
||||
return (tkimg.check(o) | repbits) & checkResolutionVariant(img, w, h, o);
|
||||
}
|
||||
|
||||
public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
|
||||
if (w == 0 || h == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Must be a ToolkitImage
|
||||
if (!(img instanceof ToolkitImage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ToolkitImage tkimg = (ToolkitImage)img;
|
||||
if (tkimg.hasError()) {
|
||||
if (o != null) {
|
||||
o.imageUpdate(img, ImageObserver.ERROR|ImageObserver.ABORT,
|
||||
-1, -1, -1, -1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ImageRepresentation ir = tkimg.getImageRep();
|
||||
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 {
|
||||
@ -818,52 +933,6 @@ public abstract class SunToolkit extends Toolkit
|
||||
sm.checkConnect(url.getHost(), url.getPort());
|
||||
}
|
||||
}
|
||||
return createImage(new URLImageSource(url));
|
||||
}
|
||||
|
||||
public Image createImage(byte[] data, int offset, int length) {
|
||||
return createImage(new ByteArrayImageSource(data, offset, length));
|
||||
}
|
||||
|
||||
public Image createImage(ImageProducer producer) {
|
||||
return new ToolkitImage(producer);
|
||||
}
|
||||
|
||||
public int checkImage(Image img, int w, int h, ImageObserver o) {
|
||||
if (!(img instanceof ToolkitImage)) {
|
||||
return ImageObserver.ALLBITS;
|
||||
}
|
||||
|
||||
ToolkitImage tkimg = (ToolkitImage)img;
|
||||
int repbits;
|
||||
if (w == 0 || h == 0) {
|
||||
repbits = ImageObserver.ALLBITS;
|
||||
} else {
|
||||
repbits = tkimg.getImageRep().check(o);
|
||||
}
|
||||
return tkimg.check(o) | repbits;
|
||||
}
|
||||
|
||||
public boolean prepareImage(Image img, int w, int h, ImageObserver o) {
|
||||
if (w == 0 || h == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Must be a ToolkitImage
|
||||
if (!(img instanceof ToolkitImage)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ToolkitImage tkimg = (ToolkitImage)img;
|
||||
if (tkimg.hasError()) {
|
||||
if (o != null) {
|
||||
o.imageUpdate(img, ImageObserver.ERROR|ImageObserver.ABORT,
|
||||
-1, -1, -1, -1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
ImageRepresentation ir = tkimg.getImageRep();
|
||||
return ir.prepare(o);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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;
|
||||
import java.awt.Rectangle;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.awt.Font;
|
||||
import java.awt.Point;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.font.GlyphVector;
|
||||
@ -93,6 +94,13 @@ import java.util.Iterator;
|
||||
import sun.misc.PerformanceLogger;
|
||||
|
||||
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
|
||||
@ -237,6 +245,7 @@ public final class SunGraphics2D
|
||||
protected Region devClip; // Actual physical drawable in pixels
|
||||
|
||||
private final int devScale; // Actual physical scale factor
|
||||
private int resolutionVariantHint;
|
||||
|
||||
// cached state for text rendering
|
||||
private boolean validFontInfo;
|
||||
@ -274,6 +283,7 @@ public final class SunGraphics2D
|
||||
lcdTextContrast = lcdTextContrastDefaultValue;
|
||||
interpolationHint = -1;
|
||||
strokeHint = SunHints.INTVAL_STROKE_DEFAULT;
|
||||
resolutionVariantHint = SunHints.INTVAL_RESOLUTION_VARIANT_DEFAULT;
|
||||
|
||||
interpolationType = AffineTransformOp.TYPE_NEAREST_NEIGHBOR;
|
||||
|
||||
@ -1249,6 +1259,10 @@ public final class SunGraphics2D
|
||||
stateChanged = (strokeHint != newHint);
|
||||
strokeHint = newHint;
|
||||
break;
|
||||
case SunHints.INTKEY_RESOLUTION_VARIANT:
|
||||
stateChanged = (resolutionVariantHint != newHint);
|
||||
resolutionVariantHint = newHint;
|
||||
break;
|
||||
default:
|
||||
recognized = false;
|
||||
stateChanged = false;
|
||||
@ -1322,6 +1336,9 @@ public final class SunGraphics2D
|
||||
case SunHints.INTKEY_STROKE_CONTROL:
|
||||
return SunHints.Value.get(SunHints.INTKEY_STROKE_CONTROL,
|
||||
strokeHint);
|
||||
case SunHints.INTKEY_RESOLUTION_VARIANT:
|
||||
return SunHints.Value.get(SunHints.INTKEY_RESOLUTION_VARIANT,
|
||||
resolutionVariantHint);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -3050,18 +3067,58 @@ public final class SunGraphics2D
|
||||
}
|
||||
// end of text rendering methods
|
||||
|
||||
private static boolean isHiDPIImage(final Image img) {
|
||||
return SurfaceManager.getImageScale(img) != 1;
|
||||
private boolean isHiDPIImage(final Image img) {
|
||||
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,
|
||||
int dy2, int sx1, int sy1, int sx2, int sy2,
|
||||
Color bgcolor, ImageObserver observer) {
|
||||
final int scale = SurfaceManager.getImageScale(img);
|
||||
sx1 = Region.clipScale(sx1, scale);
|
||||
sx2 = Region.clipScale(sx2, scale);
|
||||
sy1 = Region.clipScale(sy1, scale);
|
||||
sy2 = Region.clipScale(sy2, scale);
|
||||
|
||||
if (SurfaceManager.getImageScale(img) != 1) { // Volatile Image
|
||||
final int scale = SurfaceManager.getImageScale(img);
|
||||
sx1 = Region.clipScale(sx1, 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 {
|
||||
return imagepipe.scaleImage(this, img, dx1, dy1, dx2, dy2, sx1, sy1,
|
||||
sx2, sy2, bgcolor, observer);
|
||||
@ -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
|
||||
* callback object.
|
||||
|
620
jdk/test/java/awt/image/MultiResolutionImageTest.java
Normal file
620
jdk/test/java/awt/image/MultiResolutionImageTest.java
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user