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 backingStore = factory.createRTTexture(WIDTH + WIDTH, HEIGHT, WrapMode.CLAMP_NOT_NEEDED); 62 backingStore.contentsUseful(); 63 backingStore.makePermanent(); 64 factory.setRegionTexture(backingStore); 65 // Subdivide the texture in two halves where on half is used to store 66 // horizontal regions and the other vertical regions. Otherwise, mixing 67 // horizontal and vertical regions on the same area, would result in 68 // a lot of waste texture space. 69 hPacker = new RectanglePacker(backingStore, 0, 0, WIDTH, HEIGHT, false); 70 vPacker = new RectanglePacker(backingStore, WIDTH, 0, WIDTH, HEIGHT, true); 71 } 72 73 /** 74 * Check if the image size is to big to be stored in the cache 75 * 76 * @param w The image width 77 * @param h The image height 78 * @return True if the image size is less than max 79 */ 80 boolean isImageCachable(int w, int h) { 81 return 0 < w && w < WIDTH && 82 0 < h && h < HEIGHT && 83 (w * h) < MAX_SIZE; 84 } 85 86 RTTexture getBackingStore() { 87 return backingStore; 88 } 89 90 /** 91 * Search the cache for a background image representing the arguments. 92 * When this method succeeds the x and y coordinates in rect are adjust 93 * to the location in the backing store when the image is stored. 94 * If a failure occurred the rect is set to empty to indicate the caller 95 * to disable caching. 96 * 97 * @param key the hash key for the image 98 * @param rect the rect image. On input, width and height determine the requested 99 * texture space. On ouput, the x and y the location in the texture 100 * @param background the background used to validated if the correct image was found 101 * @param shape the shape used to validated if the correct image was found 102 * @param g the graphics to flush if the texture needs to be restarted 103 * @return true means to caller needs to render to rect to initialize the content. 104 */ 105 boolean getImageLocation(Integer key, Rectangle rect, Background background, 106 Shape shape, Graphics g) { 107 CachedImage cache = imageMap.get(key); 108 if (cache != null) { 109 if (cache.equals(rect.width, rect.height, background, shape)) { 110 rect.x = cache.x; 111 rect.y = cache.y; 112 return false; 113 } 114 // hash collision, mark rectangle empty indicates the caller to 115 // disable caching 116 rect.width = rect.height = -1; 117 return false; 118 } 119 boolean vertical = rect.height > 64; 120 RectanglePacker packer = vertical ? vPacker : hPacker; 121 122 if (!packer.add(rect)) { 123 g.sync(); 124 125 vPacker.clear(); 126 hPacker.clear(); 127 imageMap.clear(); 128 packer.add(rect); 129 backingStore.createGraphics().clear(); 130 if (PULSE_LOGGING_ENABLED) { 131 PulseLogger.incrementCounter("Region image cache flushed"); 132 } 133 } 134 imageMap.put(key, new CachedImage(rect, background, shape)); 135 return true; 136 } 137 138 static class CachedImage { 139 Background background; 140 Shape shape; 141 int x, y, width, height; 142 143 CachedImage(Rectangle rect, Background background, Shape shape) { 144 this.x = rect.x; 145 this.y = rect.y; 146 this.width = rect.width; 147 this.height = rect.height; 148 this.background = background; 149 this.shape = shape; 150 } 151 152 public boolean equals(int width, int height, Background background, Shape shape) { 153 return this.width == width && 154 this.height == height && 155 (this.background == null ? background == null : this.background.equals(background)) && 156 (this.shape == null ? shape == null : this.shape.equals(shape)); 157 } 158 } 159 160 }