1 /*
   2  * Copyright (c) 2008, 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.scenario.effect;
  27 
  28 import java.security.AccessController;
  29 import java.security.PrivilegedAction;
  30 import java.util.HashSet;
  31 import java.util.Iterator;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.scenario.effect.impl.Renderer;
  35 
  36 /**
  37  * A simple container that holds an {@code Image} and the valid source
  38  * region thereof.  Instances of {@code ImageData} can be used as the input
  39  * or output from an {@code EffectPeer}.
  40  *
  41  * Instances of this class must be validated against the {@code FilterContext}
  42  * it intended to be used with using
  43  * {@link #validate(com.sun.scenario.effect.FilterContext)} method.
  44  */
  45 public class ImageData {
  46 
  47     private static HashSet<ImageData> alldatas;
  48 
  49     static {
  50         AccessController.doPrivileged(new PrivilegedAction() {
  51             public Object run() {
  52                 if (System.getProperty("decora.showleaks") != null) {
  53                     alldatas = new HashSet<ImageData>();
  54                     Runtime.getRuntime().addShutdownHook(new Thread() {
  55                         @Override
  56                         public void run() {
  57                             Iterator<ImageData> datas = alldatas.iterator();
  58                             while (datas.hasNext()) {
  59                                 ImageData id = datas.next();
  60                                 Rectangle r = id.getUntransformedBounds();
  61                                 System.out.println("id["+r.width+"x"+r.height+", refcount="+id.refcount+"] leaked from:");
  62                                 id.fromwhere.printStackTrace(System.out);
  63                             }
  64                         }
  65                     });
  66                 }
  67                 return null;
  68             }
  69         });
  70     }
  71 
  72     private ImageData sharedOwner;
  73     private FilterContext fctx;
  74     private int refcount;
  75     private Filterable image;
  76     private final Rectangle bounds;
  77     private BaseTransform transform;
  78     private Throwable fromwhere;
  79     private boolean reusable;
  80 
  81     public ImageData(FilterContext fctx, Filterable image, Rectangle bounds) {
  82         this(fctx, image, bounds, BaseTransform.IDENTITY_TRANSFORM);
  83     }
  84 
  85     public ImageData(FilterContext fctx, Filterable image, Rectangle bounds,
  86                      BaseTransform transform)
  87     {
  88         this.fctx = fctx;
  89         this.refcount = 1;
  90         this.image = image;
  91         this.bounds = bounds;
  92         this.transform = transform;
  93         if (alldatas != null) {
  94             alldatas.add(this);
  95             this.fromwhere = new Throwable();
  96         }
  97     }
  98 
  99     public ImageData transform(BaseTransform concattx) {
 100         if (concattx.isIdentity()) {
 101             return this;
 102         }
 103         BaseTransform newtx;
 104         if (this.transform.isIdentity()) {
 105             newtx = concattx;
 106         } else {
 107             newtx = concattx.copy().deriveWithConcatenation(this.transform);
 108         }
 109         return new ImageData(this, newtx, bounds);
 110     }
 111 
 112     private ImageData(ImageData original, BaseTransform transform,
 113                       Rectangle bounds)
 114     {
 115         this(original.fctx, original.image, bounds, transform);
 116         this.sharedOwner = original;
 117     }
 118 
 119     public void setReusable(boolean reusable) {
 120         if (sharedOwner != null) {
 121             throw new InternalError("cannot make a shared ImageData reusable");
 122         }
 123         this.reusable = reusable;
 124     }
 125 
 126     public FilterContext getFilterContext() {
 127         return fctx;
 128     }
 129 
 130     public Filterable getUntransformedImage() {
 131         return image;
 132     }
 133 
 134     public Rectangle getUntransformedBounds() {
 135         return bounds;
 136     }
 137 
 138     public BaseTransform getTransform() {
 139         return transform;
 140     }
 141 
 142     public Filterable getTransformedImage(Rectangle clip) {
 143         if (image == null || fctx == null) {
 144             return null;
 145         }
 146         if (transform.isIdentity()) {
 147             return image;
 148         } else {
 149             Rectangle txbounds = getTransformedBounds(clip);
 150             return Renderer.getRenderer(fctx).
 151                 transform(fctx, image, transform, bounds, txbounds);
 152         }
 153     }
 154 
 155     public void releaseTransformedImage(Filterable tximage) {
 156         if (fctx != null && tximage != null && tximage != image) {
 157             Effect.releaseCompatibleImage(fctx, tximage);
 158         }
 159     }
 160 
 161     public Rectangle getTransformedBounds(Rectangle clip) {
 162         if (transform.isIdentity()) {
 163             return bounds;
 164         }
 165         Rectangle txbounds = new Rectangle();
 166         transform.transform(bounds, txbounds);
 167         if (clip != null) {
 168             txbounds.intersectWith(clip);
 169         }
 170         return txbounds;
 171     }
 172 
 173     public int getReferenceCount() {
 174         return refcount;
 175     }
 176 
 177     public boolean addref() {
 178         if (reusable && refcount == 0) {
 179             if (image != null) {
 180                 image.lock();
 181             }
 182         }
 183         ++refcount;
 184         return image != null && !image.isLost();
 185     }
 186 
 187     public void unref() {
 188         if (--refcount == 0) {
 189             if (sharedOwner != null) {
 190                 sharedOwner.unref();
 191                 sharedOwner = null;
 192             } else if (fctx != null && image != null) {
 193                 if (reusable) {
 194                     image.unlock();
 195                     return;
 196                 }
 197                 Effect.releaseCompatibleImage(fctx, image);
 198             }
 199             // Just in case - to prevent releasing it twice
 200             fctx = null;
 201             image = null;
 202             if (alldatas != null) {
 203                 alldatas.remove(this);
 204             }
 205         }
 206     }
 207 
 208     /**
 209      * Validates this image data for the use with the passed
 210      * {@link FilterContext}.
 211      *
 212      * @param fctx context to validate against
 213      * @return {@code true} if this object is valid and compatible with
 214      * the passed {@code FilterContext}, {@code false} otherwise.
 215      */
 216     public boolean validate(FilterContext fctx) {
 217         return image != null &&
 218                Renderer.getRenderer(fctx).isImageDataCompatible(this);
 219     }
 220 
 221     @Override
 222     public String toString() {
 223         return "ImageData{" +
 224                 "sharedOwner=" + sharedOwner +
 225                 ", fctx=" + fctx +
 226                 ", refcount=" + refcount +
 227                 ", image=" + image +
 228                 ", bounds=" + bounds +
 229                 ", transform=" + transform +
 230                 ", reusable=" + reusable + '}';
 231     }
 232 }