/*
 * 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.
/** * 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<Integer, CachedImage> 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
Params:
  • w – The image width
  • h – The image height
Returns:True if the image size is less than max
/** * 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.
Params:
  • key – the hash key for the image
  • 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
  • background – the background used to validated if the correct image was found
  • shape – the shape used to validated if the correct image was found
  • g – the graphics to flush if the texture needs to be restarted
Returns:true means to caller needs to render to rect to initialize the content.
/** * 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)); } } }