1 /* 2 * Copyright (c) 2012, 2014, 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 static com.sun.javafx.logging.PulseLogger.PULSE_LOGGING_ENABLED; 34 import com.sun.prism.Graphics; 35 import com.sun.prism.RTTexture; 36 import com.sun.prism.ResourceFactory; 37 import com.sun.prism.Texture.WrapMode; 38 import com.sun.prism.impl.packrect.RectanglePacker; 39 40 /** 41 * RegionImageCache - A fixed pixel count sized cache of Images keyed by arbitrary set of arguments. All images are held with 42 * SoftReferences so they will be dropped by the GC if heap memory gets tight. When our size hits max pixel count least 43 * recently requested images are removed first. 44 * 45 */ 46 class RegionImageCache { 47 48 // Maximum cached image size in pixels 49 private final static int MAX_SIZE = 300 * 300; 50 private static final int WIDTH = 1024; 51 private static final int HEIGHT = 1024; 52 53 private HashMap<Integer, CachedImage> imageMap; 54 private RTTexture backingStore; 55 private RectanglePacker hPacker; 56 private RectanglePacker vPacker; 57 58 59 RegionImageCache(final ResourceFactory factory) { 60 imageMap = new HashMap<>(); 61 WrapMode mode; 62 int pad; 63 if (factory.isWrapModeSupported(WrapMode.CLAMP_TO_ZERO)) { 64 mode = WrapMode.CLAMP_TO_ZERO; 65 pad = 0; 66 } else { 67 mode = WrapMode.CLAMP_NOT_NEEDED; 68 pad = 1; 69 } 70 backingStore = factory.createRTTexture(WIDTH + WIDTH, HEIGHT, mode); 71 backingStore.contentsUseful(); 72 backingStore.makePermanent(); 73 factory.setRegionTexture(backingStore); 74 // Subdivide the texture in two halves where on half is used to store 75 // horizontal regions and the other vertical regions. Otherwise, mixing 76 // horizontal and vertical regions on the same area, would result in 77 // a lot of waste texture space. 78 // Note that requests are already padded on the right and bottom edges 79 // (and that includes the gap between the caches) so we only have to 80 // pad top and left edges if CLAMP_TO_ZERO needs to be simulated. 81 hPacker = new RectanglePacker(backingStore, pad, pad, WIDTH-pad, HEIGHT-pad, false); 82 vPacker = new RectanglePacker(backingStore, WIDTH, pad, WIDTH, HEIGHT-pad, true); 83 } 84 85 /** 86 * Check if the image size is to big to be stored in the cache 87 * 88 * @param w The image width 89 * @param h The image height 90 * @return True if the image size is less than max 91 */ 92 boolean isImageCachable(int w, int h) { 93 return 0 < w && w < WIDTH && 94 0 < h && h < HEIGHT && 95 (w * h) < MAX_SIZE; 96 } 97 98 RTTexture getBackingStore() { 99 return backingStore; 100 } 101 102 /** 103 * Search the cache for a background image representing the arguments. 104 * When this method succeeds the x and y coordinates in rect are adjust 105 * to the location in the backing store when the image is stored. 106 * If a failure occurred the rect is set to empty to indicate the caller 107 * to disable caching. 108 * 109 * @param key the hash key for the image 110 * @param rect the rect image. On input, width and height determine the requested 111 * texture space. On ouput, the x and y the location in the texture 112 * @param background the background used to validated if the correct image was found 113 * @param shape the shape used to validated if the correct image was found 114 * @param g the graphics to flush if the texture needs to be restarted 115 * @return true means to caller needs to render to rect to initialize the content. 116 */ 117 boolean getImageLocation(Integer key, Rectangle rect, Background background, 118 Shape shape, Graphics g) { 119 CachedImage cache = imageMap.get(key); 120 if (cache != null) { 121 if (cache.equals(rect.width, rect.height, background, shape)) { 122 rect.x = cache.x; 123 rect.y = cache.y; 124 return false; 125 } 126 // hash collision, mark rectangle empty indicates the caller to 127 // disable caching 128 rect.width = rect.height = -1; 129 return false; 130 } 131 boolean vertical = rect.height > 64; 132 RectanglePacker packer = vertical ? vPacker : hPacker; 133 134 if (!packer.add(rect)) { 135 g.sync(); 136 137 vPacker.clear(); 138 hPacker.clear(); 139 imageMap.clear(); 140 packer.add(rect); 141 backingStore.createGraphics().clear(); 142 if (PULSE_LOGGING_ENABLED) { 143 PulseLogger.incrementCounter("Region image cache flushed"); 144 } 145 } 146 imageMap.put(key, new CachedImage(rect, background, shape)); 147 return true; 148 } 149 150 static class CachedImage { 151 Background background; 152 Shape shape; 153 int x, y, width, height; 154 155 CachedImage(Rectangle rect, Background background, Shape shape) { 156 this.x = rect.x; 157 this.y = rect.y; 158 this.width = rect.width; 159 this.height = rect.height; 160 this.background = background; 161 this.shape = shape; 162 } 163 164 public boolean equals(int width, int height, Background background, Shape shape) { 165 return this.width == width && 166 this.height == height && 167 (this.background == null ? background == null : this.background.equals(background)) && 168 (this.shape == null ? shape == null : this.shape.equals(shape)); 169 } 170 } 171 172 }