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 }