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 }