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.impl;
  27 
  28 import com.sun.scenario.effect.Effect;
  29 import com.sun.scenario.effect.Effect.AccelType;
  30 import com.sun.scenario.effect.FilterContext;
  31 import com.sun.scenario.effect.ImageData;
  32 import com.sun.javafx.geom.Rectangle;
  33 import com.sun.javafx.geom.transform.BaseTransform;
  34 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
  35 import com.sun.scenario.effect.impl.state.RenderState;
  36 
  37 /**
  38  * The abstract base class for all {@code Effect} implementation peers.
  39  * 
  40  * @param <T> an optional subclass of RenderState that can be assumed as the
  41  *            return value for the getRenderState() method
  42  */
  43 public abstract class EffectPeer<T extends RenderState> {
  44 
  45     private final FilterContext fctx;
  46     private final Renderer renderer;
  47     private final String uniqueName;
  48     private Effect effect;
  49     private T renderState;
  50     private int pass;
  51 
  52     protected EffectPeer(FilterContext fctx, Renderer renderer, String uniqueName) {
  53         if (fctx == null) {
  54             throw new IllegalArgumentException("FilterContext must be non-null");
  55         }
  56         this.fctx = fctx;
  57         this.renderer = renderer;
  58         this.uniqueName = uniqueName;
  59     }
  60 
  61     public boolean isImageDataCompatible(ImageData id) {
  62         return getRenderer().isImageDataCompatible(id);
  63     }
  64 
  65     public abstract ImageData filter(Effect effect,
  66                                      T renderState,
  67                                      BaseTransform transform,
  68                                      Rectangle outputClip,
  69                                      ImageData... inputs);
  70 
  71     /**
  72      * Disposes resources associated with this peer.
  73      * Warning: may be called from the rendering thread.
  74      */
  75     public void dispose() {
  76     }
  77 
  78     public AccelType getAccelType() {
  79         return renderer.getAccelType();
  80     }
  81 
  82     protected final FilterContext getFilterContext() {
  83         return fctx;
  84     }
  85 
  86     protected Renderer getRenderer() {
  87         return renderer;
  88     }
  89 
  90     /**
  91      * Returns the unique name of this peer.  This value can be used as
  92      * the key value in a hashmap of cached peer instances.  In the case
  93      * of hardware peers, this value is typically the name of the shader that
  94      * is used by the peer.
  95      *
  96      * @return the unique name of this peer
  97      */
  98     public String getUniqueName() {
  99         return uniqueName;
 100     }
 101 
 102     protected Effect getEffect() {
 103         return effect;
 104     }
 105 
 106     protected void setEffect(Effect effect) {
 107         this.effect = effect;
 108     }
 109 
 110     protected T getRenderState() {
 111         return renderState;
 112     }
 113 
 114     protected void setRenderState(T renderState) {
 115         this.renderState = renderState;
 116     }
 117 
 118     public final int getPass() {
 119         return pass;
 120     }
 121 
 122     public void setPass(int pass) {
 123         this.pass = pass;
 124     }
 125 
 126     // NOTE: this input(Native)Bounds stuff is unpleasant, but we somehow
 127     // need to provide access to the native surface bounds for various glue
 128     // methods (e.g. getKvals())
 129 
 130     private final Rectangle[] inputBounds = new Rectangle[2];
 131     /**
 132      * Returns the "valid" bounds of the source image for the given input.
 133      * Since Effect implementations try to recycle temporary Images, it is
 134      * quite possible that the input bounds returned by this method will
 135      * differ from the size of the associated input Image.  For example,
 136      * this method may return (0, 0, 210, 180) even though the associated
 137      * Image has dimensions of 230x200 pixels.  Pixels in the input Image
 138      * outside these "valid" bounds are undefined and should be avoided.
 139      *
 140      * @param inputIndex the index of the source input
 141      * @return the valid bounds of the source Image
 142      */
 143     protected final Rectangle getInputBounds(int inputIndex) {
 144         return inputBounds[inputIndex];
 145     }
 146     protected final void setInputBounds(int inputIndex, Rectangle r) {
 147         inputBounds[inputIndex] = r;
 148     }
 149 
 150     private final BaseTransform[] inputTransforms = new BaseTransform[2];
 151     protected final BaseTransform getInputTransform(int inputIndex) {
 152         return inputTransforms[inputIndex];
 153     }
 154     protected final void setInputTransform(int inputIndex, BaseTransform tx) {
 155         inputTransforms[inputIndex] = tx;
 156     }
 157 
 158     private final Rectangle[] inputNativeBounds = new Rectangle[2];
 159     /**
 160      * Returns the bounds of the native surface for the given input.
 161      * It is quite possible that the input native bounds returned by this
 162      * method will differ from the size of the associated input (Java-level)
 163      * Image.  This is common for the OGL and D3D backends of Java 2D,
 164      * where on older hardware the dimensions of a VRAM surface (e.g. texture)
 165      * must be a power of two.  For example, this method may return
 166      * (0, 0, 256, 256) even though the associated (Volatile)Image has
 167      * dimensions of 230x200 pixels.
 168      * <p>
 169      * This method is useful in cases where it is necessary to access
 170      * adjacent pixels in a native surface.  For example, the horizontal
 171      * distance between two texel centers of a native surface can be
 172      * calculated as (1f/inputNativeBounds.width); for the vertical distance,
 173      * (1f/inputNativeBounds.height).
 174      *
 175      * @param inputIndex the index of the source input
 176      * @return the native surface bounds
 177      */
 178     protected final Rectangle getInputNativeBounds(int inputIndex) {
 179         return inputNativeBounds[inputIndex];
 180     }
 181     protected final void setInputNativeBounds(int inputIndex, Rectangle r) {
 182         inputNativeBounds[inputIndex] = r;
 183     }
 184 
 185     public Rectangle getResultBounds(BaseTransform transform,
 186                                      Rectangle outputClip,
 187                                      ImageData... inputDatas)
 188     {
 189         return getEffect().getResultBounds(transform, outputClip, inputDatas);
 190     }
 191 
 192     /**
 193      * Returns an array of four floats that represent the mapping of the
 194      * data for the specified input to the given effect area.
 195      * The interpretation of the returned values is entirely dependent on
 196      * the algorithm of the pixel shader, but typical values are in the
 197      * "unit" coordinate space of the destination effect area, where
 198      * {@code (0,0)} is at the upper-left corner, and {@code (1,1)} is at
 199      * the lower-right corner.
 200      * The returned array contains the values in order (x1, y1, x2, y2).
 201      * <p>
 202      * The default implementation converts the logical destination effect
 203      * region into the coordinate space of the native surface of the
 204      * specified source input according to the
 205      * {@link getSourceRegion(Rectangle, Rectangle, Rectangle)} method.
 206      * <p>
 207      * Subclasses can override this method to provide more sophisticated
 208      * positioning behavior.
 209      *
 210      * @param inputIndex the index of the source input
 211      * @return an array of four float values
 212      */
 213     protected float[] getSourceRegion(int inputIndex)
 214     {
 215         return getSourceRegion(getInputBounds(inputIndex),
 216                                getInputNativeBounds(inputIndex),
 217                                getDestBounds());
 218     }
 219 
 220     /**
 221      * Returns an array of four floats that represent the mapping of the
 222      * specified source region for the specified effect area.
 223      * The returned values are in the "unit" coordinate space of the source
 224      * native surface, where (0,0) is at the upper-left corner, and (1,1)
 225      * is at the lower-right corner.
 226      * For example, if the native input surface (i.e. texture) is 256x256
 227      * pixels, and the effect output region is at the same coordinates as
 228      * the input region and is 200x200, this method will
 229      * return (0, 0, 200/256, 220/256).
 230      * The returned array contains the values in order (x1, y1, x2, y2).
 231      * <p>
 232      * Subclasses can override this method to provide more sophisticated
 233      * positioning behavior.
 234      *
 235      * @param srcBounds the logical bounds of the input data
 236      * @param srcNativeBounds the actual dimensions of the input image
 237      *                        containing the input data in its upper left
 238      * @param dstBounds the logical bounds of the resulting effect output
 239      * @return an array of four float values
 240      */
 241     static float[] getSourceRegion(Rectangle srcBounds,
 242                                    Rectangle srcNativeBounds,
 243                                    Rectangle dstBounds)
 244     {
 245         float x1 = dstBounds.x - srcBounds.x;
 246         float y1 = dstBounds.y - srcBounds.y;
 247         float x2 = x1 + dstBounds.width;
 248         float y2 = y1 + dstBounds.height;
 249         float sw = srcNativeBounds.width;
 250         float sh = srcNativeBounds.height;
 251         return new float[] {x1 / sw, y1 / sh, x2 / sw, y2 / sh};
 252     }
 253 
 254     /**
 255      * Returns either 4 or 8 source texture coordinates depending on the
 256      * transform being applied to the source.
 257      * <p>
 258      * If the mapping is rectilinear then 4 floats are returned.  The
 259      * texture coordinates are thus mapped using the following table:
 260      * <pre>
 261      *     dx1,dy1 => ret[0], ret[1]
 262      *     dx2,dy1 => ret[2], ret[1]
 263      *     dx1,dy2 => ret[0], ret[3]
 264      *     dx2,dy2 => ret[2], ret[3]
 265      * </pre>
 266      * If the mapping is non-rectilinear then 8 floats are returned and
 267      * the texture coordinates are mapped using the following table (note
 268      * that the dx1,dy1 and dx2,dy2 mappings are still from the same
 269      * indices as in the 4 float return value):
 270      * <pre>
 271      *     dx1,dy1 => ret[0], ret[1]
 272      *     dx2,dy1 => ret[4], ret[5]
 273      *     dx1,dy2 => ret[6], ret[7]
 274      *     dx2,dy2 => ret[2], ret[3]
 275      * </pre>
 276      * The default implementation of this method simply calls the static
 277      * method {@link getTextureCoordinates(float[],float,float,float,float,Rectangle,BaseTransform)}.
 278      *
 279      * @param inputIndex the index of the input whose texture coordinates
 280      *                   are being queried
 281      * @param coords An array that can hold up to 8 floats for returning
 282      *               the texture coordinates.
 283      * @param srcX The X coordinate of the origin of the source texture
 284      *             in the untransformed coordinate space.
 285      * @param srcY The Y coordinate of the origin of the source texture
 286      *             in the untransformed coordinate space.
 287      * @param srcNativeWidth the native width of the source texture
 288      * @param srcNativeHeight the native height of the source texture
 289      * @param dstBounds the output bounds that the texture is
 290      *                  being stretched over
 291      * @param transform the transform to be implicitly applied to the
 292      *                  source texture as it is mapped onto the destination
 293      * @return the number of texture coordinates stored in the {@code coords}
 294      *         array (either 4 or 8)
 295      */
 296     public int getTextureCoordinates(int inputIndex, float coords[],
 297                                      float srcX, float srcY,
 298                                      float srcNativeWidth,
 299                                      float srcNativeHeight,
 300                                      Rectangle dstBounds,
 301                                      BaseTransform transform)
 302     {
 303         return getTextureCoordinates(coords,
 304                                      srcX, srcY,
 305                                      srcNativeWidth, srcNativeHeight,
 306                                      dstBounds, transform);
 307     }
 308 
 309     /**
 310      * Returns either 4 or 8 source texture coordinates depending on the
 311      * transform being applied to the source.
 312      * <p>
 313      * If the mapping is rectilinear then 4 floats are returned.  The
 314      * texture coordinates are thus mapped using the following table:
 315      * <pre>
 316      *     dx1,dy1 => ret[0], ret[1]
 317      *     dx2,dy1 => ret[2], ret[1]
 318      *     dx1,dy2 => ret[0], ret[3]
 319      *     dx2,dy2 => ret[2], ret[3]
 320      * </pre>
 321      * If the mapping is non-rectilinear then 8 floats are returned and
 322      * the texture coordinates are mapped using the following table (note
 323      * that the dx1,dy1 and dx2,dy2 mappings are still from the same
 324      * indices as in the 4 float return value):
 325      * <pre>
 326      *     dx1,dy1 => ret[0], ret[1]
 327      *     dx2,dy1 => ret[4], ret[5]
 328      *     dx1,dy2 => ret[6], ret[7]
 329      *     dx2,dy2 => ret[2], ret[3]
 330      * </pre>
 331      *
 332      * @param coords An array that can hold up to 8 floats for returning
 333      *               the texture coordinates.
 334      * @param srcX The X coordinate of the origin of the source texture
 335      *             in the untransformed coordinate space.
 336      * @param srcY The Y coordinate of the origin of the source texture
 337      *             in the untransformed coordinate space.
 338      * @param srcNativeWidth the native width of the source texture
 339      * @param srcNativeHeight the native height of the source texture
 340      * @param dstBounds the output bounds that the texture is
 341      *                  being stretched over
 342      * @param transform the transform to be implicitly applied to the
 343      *                  source texture as it is mapped onto the destination
 344      * @return the number of texture coordinates stored in the {@code coords}
 345      *         array (either 4 or 8)
 346      */
 347     public static int getTextureCoordinates(float coords[],
 348                                             float srcX, float srcY,
 349                                             float srcNativeWidth,
 350                                             float srcNativeHeight,
 351                                             Rectangle dstBounds,
 352                                             BaseTransform transform)
 353     {
 354         coords[0] = dstBounds.x;
 355         coords[1] = dstBounds.y;
 356         coords[2] = coords[0] + dstBounds.width;
 357         coords[3] = coords[1] + dstBounds.height;
 358         int numCoords;
 359         if (transform.isTranslateOrIdentity()) {
 360             srcX += (float) transform.getMxt();
 361             srcY += (float) transform.getMyt();
 362             numCoords = 4;
 363         } else {
 364             coords[4] = coords[2];
 365             coords[5] = coords[1];
 366             coords[6] = coords[0];
 367             coords[7] = coords[3];
 368             numCoords = 8;
 369             try {
 370                 transform.inverseTransform(coords, 0, coords, 0, 4);
 371             } catch (NoninvertibleTransformException e) {
 372                 coords[0] = coords[1] = coords[2] = coords[4] = 0f;
 373                 return 4;
 374             }
 375         }
 376         for (int i = 0; i < numCoords; i += 2) {
 377             coords[i  ] = (coords[i  ] - srcX) / srcNativeWidth;
 378             coords[i+1] = (coords[i+1] - srcY) / srcNativeHeight;
 379         }
 380         return numCoords;
 381     }
 382 
 383     private Rectangle destBounds;
 384     protected final void setDestBounds(Rectangle r) {
 385         destBounds = r;
 386     }
 387     protected final Rectangle getDestBounds() {
 388         return destBounds;
 389     }
 390 
 391     private final Rectangle destNativeBounds = new Rectangle();
 392     protected final Rectangle getDestNativeBounds() {
 393         return destNativeBounds;
 394     }
 395     protected final void setDestNativeBounds(int w, int h) {
 396         destNativeBounds.width = w;
 397         destNativeBounds.height = h;
 398     }
 399 
 400     protected Object getSamplerData(int i) {
 401         return null;
 402     }
 403 
 404     /**
 405      * Returns true if the native coordinate system has its origin at
 406      * the upper-left corner of the destination surface; otherwise, returns
 407      * false, indicating that the origin is at the lower-left corner.
 408      * <p>
 409      * This method may be useful in determining the direction of adjacent
 410      * pixels in an OpenGL surface (since many OpenGL methods take parameters
 411      * assuming a lower-left origin).
 412      *
 413      * @return true if the coordinate system has an upper-left origin
 414      */
 415     protected boolean isOriginUpperLeft() {
 416         return (getAccelType() != Effect.AccelType.OPENGL);
 417     }
 418 }