1 /* 2 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.sg.prism; 27 28 import javafx.scene.layout.Background; 29 import java.util.HashMap; 30 import com.sun.javafx.geom.Rectangle; 31 import com.sun.javafx.geom.Shape; 32 import com.sun.javafx.logging.PulseLogger; 33 import com.sun.prism.Graphics; 34 import com.sun.prism.RTTexture; 35 import com.sun.prism.ResourceFactory; 36 import com.sun.prism.Texture.WrapMode; 37 import com.sun.prism.impl.packrect.RectanglePacker; 38 39 /** 40 * RegionImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with 41 * SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least 42 * recently requested images are removed first. 43 * 44 */ 45 class RegionImageCache { 46 47 // Maximum cached image size in pixels 48 private final static int MAX_SIZE = 300 * 300; 49 private static final int WIDTH = 1024; 50 private static final int HEIGHT = 1024; 51 52 private HashMap<Integer, CachedImage> imageMap; 53 private RTTexture backingStore; 54 private RectanglePacker hPacker; 55 private RectanglePacker vPacker; 56 57 58 RegionImageCache(final ResourceFactory factory) { 59 imageMap = new HashMap<>(); 60 backingStore = factory.createRTTexture(WIDTH + WIDTH, HEIGHT, WrapMode.CLAMP_NOT_NEEDED); 61 backingStore.contentsUseful(); 62 backingStore.makePermanent(); 63 factory.setRegionTexture(backingStore); 64 // Subdivide the texture in two halves where on half is used to store 65 // horizontal regions and the other vertical regions. Otherwise, mixing 66 // horizontal and vertical regions on the same area, would result in 67 // a lot of waste texture space. 68 hPacker = new RectanglePacker(backingStore, 0, 0, WIDTH, HEIGHT, false); 69 vPacker = new RectanglePacker(backingStore, WIDTH, 0, WIDTH, HEIGHT, true); 70 } 71 72 /** 73 * Check if the image size is to big to be stored in the cache 74 * 75 * @param w The image width 76 * @param h The image height 77 * @return True if the image size is less than max 78 */ 79 boolean isImageCachable(int w, int h) { 80 return 0 < w && w < WIDTH && 81 0 < h && h < HEIGHT && 82 (w * h) < MAX_SIZE; 83 } 84 85 RTTexture getBackingStore() { 86 return backingStore; 87 } 88 89 /** 90 * Search the cache for a background image representing the arguments. 91 * When this method succeeds the x and y coordinates in rect are adjust 92 * to the location in the backing store when the image is stored. 93 * If a failure occurred the rect is set to empty to indicate the caller 94 * to disable caching. 95 * 96 * @param key the hash key for the image 97 * @param rect the rect image. On input, width and height determine the requested 98 * texture space. On ouput, the x and y the location in the texture 99 * @param background the background used to validated if the correct image was found 100 * @param shape the shape used to validated if the correct image was found 101 * @param g the graphics to flush if the texture needs to be restarted 102 * @return true means to caller needs to render to rect to initialize the content. 103 */ 104 boolean getImageLocation(Integer key, Rectangle rect, Background background, 105 Shape shape, Graphics g) { 106 CachedImage cache = imageMap.get(key); 107 if (cache != null) { 108 if (cache.equals(rect.width, rect.height, background, shape)) { 109 rect.x = cache.x; 110 rect.y = cache.y; 111 return false; 112 } 113 // hash collision, mark rectangle empty indicates the caller to 114 // disable caching 115 rect.width = rect.height = -1; 116 return false; 117 } 118 boolean vertical = rect.height > 64; 119 RectanglePacker packer = vertical ? vPacker : hPacker; 120 121 if (!packer.add(rect)) { 122 g.sync(); 123 124 vPacker.clear(); 125 hPacker.clear(); 126 imageMap.clear(); 127 packer.add(rect); 128 backingStore.createGraphics().clear(); 129 if (PulseLogger.PULSE_LOGGING_ENABLED) { 130 PulseLogger.PULSE_LOGGER.renderIncrementCounter("Region image cache flushed"); 131 } 132 } 133 imageMap.put(key, new CachedImage(rect, background, shape)); 134 return true; 135 } 136 137 static class CachedImage { 138 Background background; 139 Shape shape; 140 int x, y, width, height; 141 142 CachedImage(Rectangle rect, Background background, Shape shape) { 143 this.x = rect.x; 144 this.y = rect.y; 145 this.width = rect.width; 146 this.height = rect.height; 147 this.background = background; 148 this.shape = shape; 149 } 150 151 public boolean equals(int width, int height, Background background, Shape shape) { 152 return this.width == width && 153 this.height == height && 154 (this.background == null ? background == null : this.background.equals(background)) && 155 (this.shape == null ? shape == null : this.shape.equals(shape)); 156 } 157 } 158 159 }