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 }