com.sun.javafx.sg.prism.RegionImageCache Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. 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 com.sun.javafx.sg.prism;
import javafx.scene.layout.Background;
import java.util.HashMap;
import com.sun.javafx.geom.Rectangle;
import com.sun.javafx.geom.Shape;
import com.sun.javafx.logging.PulseLogger;
import static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED;
import com.sun.prism.Graphics;
import com.sun.prism.RTTexture;
import com.sun.prism.ResourceFactory;
import com.sun.prism.Texture.WrapMode;
import com.sun.prism.impl.packrect.RectanglePacker;
/**
* RegionImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with
* SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least
* recently requested images are removed first.
*
*/
class RegionImageCache {
// Maximum cached image size in pixels
private final static int MAX_SIZE = 300 * 300;
private static final int WIDTH = 1024;
private static final int HEIGHT = 1024;
private HashMap imageMap;
private RTTexture backingStore;
private RectanglePacker hPacker;
private RectanglePacker vPacker;
RegionImageCache(final ResourceFactory factory) {
imageMap = new HashMap<>();
WrapMode mode;
int pad;
if (factory.isWrapModeSupported(WrapMode.CLAMP_TO_ZERO)) {
mode = WrapMode.CLAMP_TO_ZERO;
pad = 0;
} else {
mode = WrapMode.CLAMP_NOT_NEEDED;
pad = 1;
}
backingStore = factory.createRTTexture(WIDTH + WIDTH, HEIGHT, mode);
backingStore.contentsUseful();
backingStore.makePermanent();
factory.setRegionTexture(backingStore);
// Subdivide the texture in two halves where on half is used to store
// horizontal regions and the other vertical regions. Otherwise, mixing
// horizontal and vertical regions on the same area, would result in
// a lot of waste texture space.
// Note that requests are already padded on the right and bottom edges
// (and that includes the gap between the caches) so we only have to
// pad top and left edges if CLAMP_TO_ZERO needs to be simulated.
hPacker = new RectanglePacker(backingStore, pad, pad, WIDTH-pad, HEIGHT-pad, false);
vPacker = new RectanglePacker(backingStore, WIDTH, pad, WIDTH, HEIGHT-pad, true);
}
/**
* Check if the image size is to big to be stored in the cache
*
* @param w The image width
* @param h The image height
* @return True if the image size is less than max
*/
boolean isImageCachable(int w, int h) {
return 0 < w && w < WIDTH &&
0 < h && h < HEIGHT &&
(w * h) < MAX_SIZE;
}
RTTexture getBackingStore() {
return backingStore;
}
/**
* Search the cache for a background image representing the arguments.
* When this method succeeds the x and y coordinates in rect are adjust
* to the location in the backing store when the image is stored.
* If a failure occurred the rect is set to empty to indicate the caller
* to disable caching.
*
* @param key the hash key for the image
* @param rect the rect image. On input, width and height determine the requested
* texture space. On ouput, the x and y the location in the texture
* @param background the background used to validated if the correct image was found
* @param shape the shape used to validated if the correct image was found
* @param g the graphics to flush if the texture needs to be restarted
* @return true means to caller needs to render to rect to initialize the content.
*/
boolean getImageLocation(Integer key, Rectangle rect, Background background,
Shape shape, Graphics g) {
CachedImage cache = imageMap.get(key);
if (cache != null) {
if (cache.equals(rect.width, rect.height, background, shape)) {
rect.x = cache.x;
rect.y = cache.y;
return false;
}
// hash collision, mark rectangle empty indicates the caller to
// disable caching
rect.width = rect.height = -1;
return false;
}
boolean vertical = rect.height > 64;
RectanglePacker packer = vertical ? vPacker : hPacker;
if (!packer.add(rect)) {
g.sync();
vPacker.clear();
hPacker.clear();
imageMap.clear();
packer.add(rect);
backingStore.createGraphics().clear();
if (PULSE_LOGGING_ENABLED) {
PulseLogger.incrementCounter("Region image cache flushed");
}
}
imageMap.put(key, new CachedImage(rect, background, shape));
return true;
}
static class CachedImage {
Background background;
Shape shape;
int x, y, width, height;
CachedImage(Rectangle rect, Background background, Shape shape) {
this.x = rect.x;
this.y = rect.y;
this.width = rect.width;
this.height = rect.height;
this.background = background;
this.shape = shape;
}
public boolean equals(int width, int height, Background background, Shape shape) {
return this.width == width &&
this.height == height &&
(this.background == null ? background == null : this.background.equals(background)) &&
(this.shape == null ? shape == null : this.shape.equals(shape));
}
}
}