提交 339f35c4 编写于 作者: A alexsch

8035069: [macosx] Loading resolution variants by demand

Reviewed-by: serb, pchelko
上级 4393ad17
...@@ -295,14 +295,8 @@ public class AquaIcon { ...@@ -295,14 +295,8 @@ public class AquaIcon {
} }
Image createImage() { Image createImage() {
int w = getIconWidth(); return AquaUtils.getCImageCreator().createSystemImageFromSelector(
int h = getIconHeight(); selector, getIconWidth(), getIconHeight());
return new AquaImageFactory.MultiResolutionIconImage(
AquaUtils.getCImageCreator().createSystemImageFromSelector(
selector, w, h),
AquaUtils.getCImageCreator().createSystemImageFromSelector(
selector, 2 * w, 2 * h)
);
} }
} }
} }
...@@ -125,16 +125,14 @@ public class AquaImageFactory { ...@@ -125,16 +125,14 @@ public class AquaImageFactory {
private static final int kAlertIconSize = 64; private static final int kAlertIconSize = 64;
static IconUIResource getAppIconCompositedOn(final Image background) { static IconUIResource getAppIconCompositedOn(final Image background) {
final BufferedImage iconImage = getAppIconImageCompositedOn(background, 1); if (background instanceof MultiResolutionBufferedImage) {
int width = background.getWidth(null);
if (background instanceof MultiResolutionIconImage) { Image mrIconImage = ((MultiResolutionBufferedImage) background).map(
BufferedImage background2x rv -> getAppIconImageCompositedOn(rv, rv.getWidth(null) / width));
= ((MultiResolutionIconImage) background).resolutionVariant; return new IconUIResource(new ImageIcon(mrIconImage));
BufferedImage icon2xImage = getAppIconImageCompositedOn(background2x, 2);
return new IconUIResource(new ImageIcon(
new MultiResolutionIconImage(iconImage, icon2xImage)));
} }
BufferedImage iconImage = getAppIconImageCompositedOn(background, 1);
return new IconUIResource(new ImageIcon(iconImage)); return new IconUIResource(new ImageIcon(iconImage));
} }
...@@ -312,10 +310,16 @@ public class AquaImageFactory { ...@@ -312,10 +310,16 @@ public class AquaImageFactory {
return icon; return icon;
} }
Image icon2x = AquaUtils.getCImageCreator().createImageFromName( int w = icon.getWidth(null);
imageName, 2 * icon.getWidth(null), 2 * icon.getHeight(null)); int h = icon.getHeight(null);
return new MultiResolutionBufferedImage(
BufferedImage.TYPE_INT_ARGB_PRE, 0, icon, icon2x); Dimension[] sizes = new Dimension[]{
new Dimension(w, h), new Dimension(2 * w, 2 * h)
};
return new MultiResolutionBufferedImage(icon, sizes, (width, height) ->
AquaUtils.getCImageCreator().createImageFromName(
imageName, width, height));
} }
public static class NineSliceMetrics { public static class NineSliceMetrics {
...@@ -524,29 +528,4 @@ public class AquaImageFactory { ...@@ -524,29 +528,4 @@ public class AquaImageFactory {
public static Color getSelectionInactiveForegroundColorUIResource() { public static Color getSelectionInactiveForegroundColorUIResource() {
return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_FOREGROUND_COLOR)); return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_FOREGROUND_COLOR));
} }
}
static class MultiResolutionIconImage extends BufferedImage \ No newline at end of file
implements MultiResolutionImage {
BufferedImage resolutionVariant;
public MultiResolutionIconImage(BufferedImage image, BufferedImage resolutionVariant) {
super(image.getWidth(), image.getHeight(), image.getType());
this.resolutionVariant = resolutionVariant;
Graphics g = getGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
}
@Override
public Image getResolutionVariant(int width, int height) {
return ((width <= getWidth() && height <= getHeight()))
? this : resolutionVariant;
}
@Override
public List<Image> getResolutionVariants() {
return Arrays.asList(this, resolutionVariant);
}
}
}
...@@ -38,6 +38,7 @@ import sun.java2d.*; ...@@ -38,6 +38,7 @@ import sun.java2d.*;
import sun.print.*; import sun.print.*;
import apple.laf.*; import apple.laf.*;
import apple.laf.JRSUIUtils.NineSliceMetricsProvider; import apple.laf.JRSUIUtils.NineSliceMetricsProvider;
import sun.awt.image.ImageCache;
abstract class AquaPainter <T extends JRSUIState> { abstract class AquaPainter <T extends JRSUIState> {
static <T extends JRSUIState> AquaPainter<T> create(final T state) { static <T extends JRSUIState> AquaPainter<T> create(final T state) {
...@@ -155,10 +156,15 @@ abstract class AquaPainter <T extends JRSUIState> { ...@@ -155,10 +156,15 @@ abstract class AquaPainter <T extends JRSUIState> {
final ImageCache cache = ImageCache.getInstance(); final ImageCache cache = ImageCache.getInstance();
final int imgW = bounds.width * scale; final int imgW = bounds.width * scale;
final int imgH = bounds.height * scale; final int imgH = bounds.height * scale;
BufferedImage img = (BufferedImage) cache.getImage(config, imgW, imgH, scale, controlState); AquaPixelsKey key = new AquaPixelsKey(config,
imgW, imgH, scale, controlState);
BufferedImage img = (BufferedImage) cache.getImage(key);
if (img == null) { if (img == null) {
img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB_PRE); img = new BufferedImage(imgW, imgH, BufferedImage.TYPE_INT_ARGB_PRE);
cache.setImage(img, config, imgW, imgH, scale, controlState); if (!controlState.is(JRSUIConstants.Animating.YES)) {
cache.setImage(key, img);
}
final WritableRaster raster = img.getRaster(); final WritableRaster raster = img.getRaster();
final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer(); final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();
...@@ -172,6 +178,59 @@ abstract class AquaPainter <T extends JRSUIState> { ...@@ -172,6 +178,59 @@ abstract class AquaPainter <T extends JRSUIState> {
} }
} }
private static class AquaPixelsKey implements ImageCache.PixelsKey {
private final int pixelCount;
private final int hash;
// key parts
private final GraphicsConfiguration config;
private final int w;
private final int h;
private final int scale;
private final JRSUIState state;
AquaPixelsKey(final GraphicsConfiguration config,
final int w, final int h, final int scale,
final JRSUIState state) {
this.pixelCount = w * h;
this.config = config;
this.w = w;
this.h = h;
this.scale = scale;
this.state = state;
this.hash = hash();
}
public int getPixelCount() {
return pixelCount;
}
private int hash() {
int hash = config != null ? config.hashCode() : 0;
hash = 31 * hash + w;
hash = 31 * hash + h;
hash = 31 * hash + scale;
hash = 31 * hash + state.hashCode();
return hash;
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof AquaPixelsKey) {
AquaPixelsKey key = (AquaPixelsKey) obj;
return config == key.config && w == key.w && h == key.h
&& scale == key.scale && state.equals(key.state);
}
return false;
}
}
private static class RecyclableJRSUISlicedImageControl private static class RecyclableJRSUISlicedImageControl
extends RecyclableSlicedImageControl { extends RecyclableSlicedImageControl {
......
...@@ -177,16 +177,7 @@ final class AquaUtils { ...@@ -177,16 +177,7 @@ final class AquaUtils {
abstract static class RecyclableSingleton<T> { abstract static class RecyclableSingleton<T> {
final T get() { final T get() {
final AppContext appContext = AppContext.getAppContext(); return AppContext.getSoftReferenceValue(this, () -> getInstance());
SoftReference<T> ref = (SoftReference<T>) appContext.get(this);
if (ref != null) {
final T object = ref.get();
if (object != null) return object;
}
final T object = getInstance();
ref = new SoftReference<T>(object);
appContext.put(this, ref);
return object;
} }
void reset() { void reset() {
......
...@@ -224,24 +224,11 @@ public class CImage extends CFRetainedResource { ...@@ -224,24 +224,11 @@ public class CImage extends CFRetainedResource {
= nativeGetNSImageRepresentationSizes(ptr, = nativeGetNSImageRepresentationSizes(ptr,
size.getWidth(), size.getHeight()); size.getWidth(), size.getHeight());
if (sizes == null || sizes.length < 2) { BufferedImage baseImage = toImage(w, h, w, h);
return toImage(w, h, w, h);
}
BufferedImage[] images = new BufferedImage[sizes.length];
int currentImageIndex = 0;
for (int i = 0; i < sizes.length; i++) {
int imageRepWidth = (int) sizes[i].getWidth();
int imageRepHeight = (int) sizes[i].getHeight();
if(imageRepHeight <= w && imageRepHeight <= h){ return sizes == null || sizes.length < 2 ? baseImage
currentImageIndex = i; : new MultiResolutionBufferedImage(baseImage, sizes,
} (width, height) -> toImage(w, h, width, height));
images[i] = toImage(w, h, imageRepWidth, imageRepHeight);
}
return new MultiResolutionBufferedImage(BufferedImage.TYPE_INT_ARGB_PRE,
currentImageIndex, images);
} }
private BufferedImage toImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight) { private BufferedImage toImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
......
...@@ -42,11 +42,13 @@ import java.util.Set; ...@@ -42,11 +42,13 @@ import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.lang.ref.SoftReference;
import sun.util.logging.PlatformLogger; import sun.util.logging.PlatformLogger;
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/** /**
* The AppContext is a table referenced by ThreadGroup which stores * The AppContext is a table referenced by ThreadGroup which stores
...@@ -883,6 +885,23 @@ public final class AppContext { ...@@ -883,6 +885,23 @@ public final class AppContext {
}); });
} }
public static <T> T getSoftReferenceValue(Object key,
Supplier<T> supplier) {
final AppContext appContext = AppContext.getAppContext();
SoftReference<T> ref = (SoftReference<T>) appContext.get(key);
if (ref != null) {
final T object = ref.get();
if (object != null) {
return object;
}
}
final T object = supplier.get();
ref = new SoftReference<>(object);
appContext.put(key, ref);
return object;
}
} }
final class MostRecentKeyValue { final class MostRecentKeyValue {
......
/* /*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,25 +23,28 @@ ...@@ -23,25 +23,28 @@
* questions. * questions.
*/ */
package com.apple.laf; package sun.awt.image;
import java.awt.*; import java.awt.*;
import java.lang.ref.*; import java.lang.ref.*;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.*; import java.util.concurrent.locks.*;
import sun.awt.AppContext;
import apple.laf.JRSUIConstants;
import apple.laf.JRSUIState;
import com.apple.laf.AquaUtils.RecyclableSingleton;
/** /**
* ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with * ImageCache - A fixed pixel count sized cache of Images keyed by arbitrary
* SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least * set of arguments. All images are held with SoftReferences so they will be
* recently requested images are removed first. * dropped by the GC if heap memory gets tight. When our size hits max pixel
* count least recently requested images are removed first.
*
* The ImageCache must be used from the thread with an AppContext only.
*
*/ */
final class ImageCache { final public class ImageCache {
// Ordered Map keyed by args hash, ordered by most recent accessed entry. // Ordered Map keyed by args hash, ordered by most recent accessed entry.
private final LinkedHashMap<Integer, PixelCountSoftReference> map = new LinkedHashMap<>(16, 0.75f, true); private final LinkedHashMap<PixelsKey, ImageSoftReference> map
= new LinkedHashMap<>(16, 0.75f, true);
// Maximum number of pixels to cache, this is used if maxCount // Maximum number of pixels to cache, this is used if maxCount
private final int maxPixelCount; private final int maxPixelCount;
...@@ -53,15 +56,9 @@ final class ImageCache { ...@@ -53,15 +56,9 @@ final class ImageCache {
// Reference queue for tracking lost softreferences to images in the cache // Reference queue for tracking lost softreferences to images in the cache
private final ReferenceQueue<Image> referenceQueue = new ReferenceQueue<>(); private final ReferenceQueue<Image> referenceQueue = new ReferenceQueue<>();
// Singleton Instance public static ImageCache getInstance() {
private static final RecyclableSingleton<ImageCache> instance = new RecyclableSingleton<ImageCache>() { return AppContext.getSoftReferenceValue(ImageCache.class,
@Override () -> new ImageCache());
protected ImageCache getInstance() {
return new ImageCache();
}
};
static ImageCache getInstance() {
return instance.get();
} }
ImageCache(final int maxPixelCount) { ImageCache(final int maxPixelCount) {
...@@ -81,186 +78,86 @@ final class ImageCache { ...@@ -81,186 +78,86 @@ final class ImageCache {
} }
} }
public Image getImage(final GraphicsConfiguration config, final int w, public Image getImage(final PixelsKey key){
final int h, final int scale, final ImageSoftReference ref;
final JRSUIState state) {
final int hash = hash(config, w, h, scale, state);
final PixelCountSoftReference ref;
lock.readLock().lock(); lock.readLock().lock();
try { try {
ref = map.get(hash); ref = map.get(key);
} finally { } finally {
lock.readLock().unlock(); lock.readLock().unlock();
} }
// check reference has not been lost and the key truly matches, return ref == null ? null : ref.get();
// in case of false positive hash match
if (ref != null && ref.equals(config, w, h, scale, state)) {
return ref.get();
}
return null;
} }
/** /**
* Sets the cached image for the specified constraints. * Sets the cached image for the specified constraints.
* *
* @param key The key with which the specified image is to be associated
* @param image The image to store in cache * @param image The image to store in cache
* @param config The graphics configuration, needed if cached image is a Volatile Image. Used as part of cache key
* @param w The image width, used as part of cache key
* @param h The image height, used as part of cache key
* @param scale The image scale factor, used as part of cache key
* @return true if the image could be cached, false otherwise.
*/ */
public boolean setImage(final Image image, public void setImage(final PixelsKey key, final Image image) {
final GraphicsConfiguration config, final int w, final int h,
final int scale, final JRSUIState state) {
if (state.is(JRSUIConstants.Animating.YES)) {
return false;
}
final int hash = hash(config, w, h, scale, state);
lock.writeLock().lock(); lock.writeLock().lock();
try { try {
PixelCountSoftReference ref = map.get(hash); ImageSoftReference ref = map.get(key);
// check if currently in map
if (ref != null && ref.get() == image) return true;
// clear out old // check if currently in map
if (ref != null) { if (ref != null) {
currentPixelCount -= ref.pixelCount; if (ref.get() != null) {
map.remove(hash); return;
} }
// soft image has been removed
currentPixelCount -= key.getPixelCount();
map.remove(key);
};
// add new image to pixel count // add new image to pixel count
final int newPixelCount = image.getWidth(null) * image.getHeight(null); final int newPixelCount = key.getPixelCount();
currentPixelCount += newPixelCount; currentPixelCount += newPixelCount;
// clean out lost references if not enough space // clean out lost references if not enough space
if (currentPixelCount > maxPixelCount) { if (currentPixelCount > maxPixelCount) {
while ((ref = (PixelCountSoftReference)referenceQueue.poll()) != null) { while ((ref = (ImageSoftReference)referenceQueue.poll()) != null) {
//reference lost //reference lost
map.remove(ref.hash); map.remove(ref.key);
currentPixelCount -= ref.pixelCount; currentPixelCount -= ref.key.getPixelCount();
} }
} }
// remove old items till there is enough free space // remove old items till there is enough free space
if (currentPixelCount > maxPixelCount) { if (currentPixelCount > maxPixelCount) {
final Iterator<Map.Entry<Integer, PixelCountSoftReference>> mapIter = map.entrySet().iterator(); final Iterator<Map.Entry<PixelsKey, ImageSoftReference>>
mapIter = map.entrySet().iterator();
while ((currentPixelCount > maxPixelCount) && mapIter.hasNext()) { while ((currentPixelCount > maxPixelCount) && mapIter.hasNext()) {
final Map.Entry<Integer, PixelCountSoftReference> entry = mapIter.next(); final Map.Entry<PixelsKey, ImageSoftReference> entry =
mapIter.next();
mapIter.remove(); mapIter.remove();
final Image img = entry.getValue().get(); final Image img = entry.getValue().get();
if (img != null) img.flush(); if (img != null) img.flush();
currentPixelCount -= entry.getValue().pixelCount; currentPixelCount -= entry.getValue().key.getPixelCount();
} }
} }
// finally put new in map // finally put new in map
map.put(hash, new PixelCountSoftReference(image, referenceQueue, newPixelCount, hash, config, w, h, scale, state)); map.put(key, new ImageSoftReference(key, image, referenceQueue));
return true;
} finally { } finally {
lock.writeLock().unlock(); lock.writeLock().unlock();
} }
} }
private static int hash(final GraphicsConfiguration config, final int w, public interface PixelsKey {
final int h, final int scale,
final JRSUIState state) {
int hash = config != null ? config.hashCode() : 0;
hash = 31 * hash + w;
hash = 31 * hash + h;
hash = 31 * hash + scale;
hash = 31 * hash + state.hashCode();
return hash;
}
/** int getPixelCount();
* Extended SoftReference that stores the pixel count even after the image }
* is lost.
*/
private static class PixelCountSoftReference extends SoftReference<Image> {
// default access, because access to these fields shouldn't be emulated private static class ImageSoftReference extends SoftReference<Image> {
// by a synthetic accessor.
final int pixelCount;
final int hash;
// key parts final PixelsKey key;
private final GraphicsConfiguration config;
private final int w;
private final int h;
private final int scale;
private final JRSUIState state;
PixelCountSoftReference(final Image referent, ImageSoftReference(final PixelsKey key, final Image referent,
final ReferenceQueue<? super Image> q, final int pixelCount, final ReferenceQueue<? super Image> q) {
final int hash, final GraphicsConfiguration config, final int w,
final int h, final int scale, final JRSUIState state) {
super(referent, q); super(referent, q);
this.pixelCount = pixelCount; this.key = key;
this.hash = hash;
this.config = config;
this.w = w;
this.h = h;
this.scale = scale;
this.state = state;
}
boolean equals(final GraphicsConfiguration config, final int w,
final int h, final int scale, final JRSUIState state) {
return config == this.config && w == this.w && h == this.h
&& scale == this.scale && state.equals(this.state);
} }
} }
// /** Gets the rendered image for this painter at the requested size, either from cache or create a new one */
// private VolatileImage getImage(GraphicsConfiguration config, JComponent c, int w, int h, Object[] extendedCacheKeys) {
// VolatileImage buffer = (VolatileImage)getImage(config, w, h, this, extendedCacheKeys);
//
// int renderCounter = 0; // to avoid any potential, though unlikely, infinite loop
// do {
// //validate the buffer so we can check for surface loss
// int bufferStatus = VolatileImage.IMAGE_INCOMPATIBLE;
// if (buffer != null) {
// bufferStatus = buffer.validate(config);
// }
//
// //If the buffer status is incompatible or restored, then we need to re-render to the volatile image
// if (bufferStatus == VolatileImage.IMAGE_INCOMPATIBLE || bufferStatus == VolatileImage.IMAGE_RESTORED) {
// // if the buffer isn't the right size, or has lost its contents, then recreate
// if (buffer != null) {
// if (buffer.getWidth() != w || buffer.getHeight() != h || bufferStatus == VolatileImage.IMAGE_INCOMPATIBLE) {
// // clear any resources related to the old back buffer
// buffer.flush();
// buffer = null;
// }
// }
//
// if (buffer == null) {
// // recreate the buffer
// buffer = config.createCompatibleVolatileImage(w, h, Transparency.TRANSLUCENT);
// // put in cache for future
// setImage(buffer, config, w, h, this, extendedCacheKeys);
// }
//
// //create the graphics context with which to paint to the buffer
// Graphics2D bg = buffer.createGraphics();
//
// //clear the background before configuring the graphics
// bg.setComposite(AlphaComposite.Clear);
// bg.fillRect(0, 0, w, h);
// bg.setComposite(AlphaComposite.SrcOver);
// bg.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
//
// // paint the painter into buffer
// paint0(bg, c, w, h, extendedCacheKeys);
// //close buffer graphics
// bg.dispose();
// }
// } while (buffer.contentsLost() && renderCounter++ < 3);
//
// // check if we failed
// if (renderCounter >= 3) return null;
//
// return buffer;
// }
} }
...@@ -26,46 +26,152 @@ package sun.awt.image; ...@@ -26,46 +26,152 @@ package sun.awt.image;
import java.awt.Image; import java.awt.Image;
import java.awt.Graphics; import java.awt.Graphics;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
public class MultiResolutionBufferedImage extends BufferedImage public class MultiResolutionBufferedImage extends BufferedImage
implements MultiResolutionImage { implements MultiResolutionImage {
Image[] resolutionVariants; private final BiFunction<Integer, Integer, Image> mapper;
int baseIndex; private final Dimension2D[] sizes;
private int availableInfo;
public MultiResolutionBufferedImage(int imageType, int baseIndex, Image... images) { public MultiResolutionBufferedImage(Image baseImage,
super(images[baseIndex].getWidth(null), images[baseIndex].getHeight(null), Dimension2D[] sizes, BiFunction<Integer, Integer, Image> mapper) {
imageType); super(baseImage.getWidth(null), baseImage.getHeight(null),
this.baseIndex = baseIndex; BufferedImage.TYPE_INT_ARGB_PRE);
this.resolutionVariants = images; this.sizes = sizes;
this.mapper = mapper;
this.availableInfo = getInfo(baseImage);
Graphics g = getGraphics(); Graphics g = getGraphics();
g.drawImage(images[baseIndex], 0, 0, null); g.drawImage(baseImage, 0, 0, null);
g.dispose(); g.dispose();
images[baseIndex] = this;
} }
@Override @Override
public Image getResolutionVariant(int width, int height) { public Image getResolutionVariant(int width, int height) {
for (Image image : resolutionVariants) { int baseWidth = getWidth();
if (width <= image.getWidth(null) && height <= image.getHeight(null)) { int baseHeight = getHeight();
return image;
} if (baseWidth == width && baseHeight == height) {
return this;
} }
return this;
ImageCache cache = ImageCache.getInstance();
ImageCacheKey key = new ImageCacheKey(this, width, height);
Image resolutionVariant = cache.getImage(key);
if (resolutionVariant == null) {
resolutionVariant = mapper.apply(width, height);
cache.setImage(key, resolutionVariant);
preload(resolutionVariant, availableInfo);
}
return resolutionVariant;
} }
@Override @Override
public List<Image> getResolutionVariants() { public List<Image> getResolutionVariants() {
return Arrays.asList(resolutionVariants); return Arrays.stream(sizes).map((Function<Dimension2D, Image>) size
-> getResolutionVariant((int) size.getWidth(),
(int) size.getHeight())).collect(Collectors.toList());
} }
public MultiResolutionBufferedImage map(Function<Image, Image> mapper) { public MultiResolutionBufferedImage map(Function<Image, Image> mapper) {
return new MultiResolutionBufferedImage(getType(), baseIndex, return new MultiResolutionBufferedImage(mapper.apply(this), sizes,
Arrays.stream(resolutionVariants).map(mapper) (width, height) ->
.toArray(length -> new Image[length])); mapper.apply(getResolutionVariant(width, height)));
}
@Override
public int getWidth(ImageObserver observer) {
availableInfo |= ImageObserver.WIDTH;
return super.getWidth(observer);
}
@Override
public int getHeight(ImageObserver observer) {
availableInfo |= ImageObserver.HEIGHT;
return super.getHeight(observer);
}
@Override
public Object getProperty(String name, ImageObserver observer) {
availableInfo |= ImageObserver.PROPERTIES;
return super.getProperty(name, observer);
}
private static int getInfo(Image image) {
if (image instanceof ToolkitImage) {
return ((ToolkitImage) image).getImageRep().check(
(img, infoflags, x, y, w, h) -> false);
}
return 0;
}
private static void preload(Image image, int availableInfo) {
if (image instanceof ToolkitImage) {
((ToolkitImage) image).preload(new ImageObserver() {
int flags = availableInfo;
@Override
public boolean imageUpdate(Image img, int infoflags,
int x, int y, int width, int height) {
flags &= ~infoflags;
return (flags != 0) && ((infoflags
& (ImageObserver.ERROR | ImageObserver.ABORT)) == 0);
}
});
}
}
private static class ImageCacheKey implements ImageCache.PixelsKey {
private final int pixelCount;
private final int hash;
private final int w;
private final int h;
private final Image baseImage;
ImageCacheKey(final Image baseImage,
final int w, final int h) {
this.baseImage = baseImage;
this.w = w;
this.h = h;
this.pixelCount = w * h;
hash = hash();
}
@Override
public int getPixelCount() {
return pixelCount;
}
private int hash() {
int hash = baseImage.hashCode();
hash = 31 * hash + w;
hash = 31 * hash + h;
return hash;
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ImageCacheKey) {
ImageCacheKey key = (ImageCacheKey) obj;
return baseImage == key.baseImage && w == key.w && h == key.h;
}
return false;
}
} }
} }
\ No newline at end of file
...@@ -27,7 +27,7 @@ import sun.awt.OSInfo; ...@@ -27,7 +27,7 @@ import sun.awt.OSInfo;
import sun.awt.image.MultiResolutionImage; import sun.awt.image.MultiResolutionImage;
/* /*
* @test * @test
* @bug 8033534 * @bug 8033534 8035069
* @summary [macosx] Get MultiResolution image from native system * @summary [macosx] Get MultiResolution image from native system
* @author Alexander Scherbatiy * @author Alexander Scherbatiy
* @run main NSImageToMultiResolutionImageTest * @run main NSImageToMultiResolutionImageTest
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册