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