Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.effect; 027 028import javafx.beans.Observable; 029import javafx.beans.property.BooleanProperty; 030import javafx.beans.property.BooleanPropertyBase; 031import javafx.beans.property.DoubleProperty; 032import javafx.beans.property.DoublePropertyBase; 033import javafx.beans.property.ObjectProperty; 034import javafx.beans.property.ObjectPropertyBase; 035import javafx.scene.Node; 036 037import com.sun.javafx.effect.EffectDirtyBits; 038import com.sun.javafx.effect.EffectUtils; 039import com.sun.javafx.geom.BaseBounds; 040import com.sun.javafx.geom.transform.BaseTransform; 041import com.sun.javafx.scene.BoundsAccessor; 042 043/** 044 * An effect that shifts each pixel by a distance specified by 045 * the first two bands of of the specified {@link FloatMap}. 046 * For each pixel in the output, the corresponding data from the 047 * {@code mapData} is retrieved, scaled and offset by the {@code scale} 048 * and {@code offset} attributes, scaled again by the size of the 049 * source input image and used as an offset from the destination pixel 050 * to retrieve the pixel data from the source input. 051 * <pre> 052 * dst[x,y] = src[(x,y) + (offset+scale*map[x,y])*(srcw,srch)] 053 * </pre> 054 * A value of {@code (0.0, 0.0)} would specify no offset for the 055 * pixel data whereas a value of {@code (0.5, 0.5)} would specify 056 * an offset of half of the source image size. 057 * <p> 058 * <b>Note</b> that the mapping is the offset from a destination pixel to 059 * the source pixel location from which it is sampled which means that 060 * filling the map with all values of {@code 0.5} would displace the 061 * image by half of its size towards the upper left since each destination 062 * pixel would contain the data that comes from the source pixel below and 063 * to the right of it. 064 * <p> 065 * Also note that this effect does not adjust the coordinates of input 066 * events or any methods that measure containment on a {@code Node}. 067 * The results of mouse picking and the containment methods are undefined 068 * when a {@code Node} has a {@code DisplacementMap} effect in place. 069 * 070 * <p> 071 * Example: 072 * <pre><code> 073 * int width = 220; 074 * int height = 100; 075 * 076 * FloatMap floatMap = new FloatMap(); 077 * floatMap.setWidth(width); 078 * floatMap.setHeight(height); 079 * 080 * for (int i = 0; i < width; i++) { 081 * double v = (Math.sin(i / 20.0 * Math.PI) - 0.5) / 40.0; 082 * for (int j = 0; j < height; j++) { 083 * floatMap.setSamples(i, j, 0.0f, (float) v); 084 * } 085 * } 086 * 087 * DisplacementMap displacementMap = new DisplacementMap(); 088 * displacementMap.setMapData(floatMap); 089 * 090 * Text text = new Text(); 091 * text.setX(40.0); 092 * text.setY(80.0); 093 * text.setText("Wavy Text"); 094 * text.setFill(Color.web("0x3b596d")); 095 * text.setFont(Font.font(null, FontWeight.BOLD, 50)); 096 * text.setEffect(displacementMap); 097 * 098 * </pre></code> 099 * 100 * <p> The code above produces the following: </p> 101 * <p> <img src="doc-files/displacementmap.png"/> </p> 102 */ 103public class DisplacementMap extends Effect { 104 @Override 105 com.sun.scenario.effect.DisplacementMap impl_createImpl() { 106 return new com.sun.scenario.effect.DisplacementMap( 107 new com.sun.scenario.effect.FloatMap(1, 1), 108 com.sun.scenario.effect.Effect.DefaultInput); 109 }; 110 111 /** 112 * Creates a new instance of DisplacementMap with default parameters. 113 */ 114 public DisplacementMap() { 115 setMapData(new FloatMap(1, 1)); 116 } 117 118 /** 119 * Creates a new instance of DisplacementMap with the specified mapData. 120 * @param mapData the map data for this displacement map effect 121 */ 122 public DisplacementMap(FloatMap mapData) { 123 setMapData(mapData); 124 } 125 126 /** 127 * Creates a new instance of DisplacementMap with the specified mapData, 128 * offsetX, offsetY, scaleX, and scaleY. 129 * @param mapData the map data for this displacement map effect 130 * @param offsetX the offset by which all x coordinate offset values in the 131 * {@code FloatMap} are displaced after they are scaled 132 * @param offsetY the offset by which all y coordinate offset values in the 133 * {@code FloatMap} are displaced after they are scaled 134 * @param scaleX the scale factor by which all x coordinate offset values in the 135 * {@code FloatMap} are multiplied 136 * @param scaleY the scale factor by which all y coordinate offset values in the 137 * {@code FloatMap} are multiplied 138 */ 139 public DisplacementMap(FloatMap mapData, 140 double offsetX, double offsetY, 141 double scaleX, double scaleY) { 142 setMapData(mapData); 143 setOffsetX(offsetX); 144 setOffsetY(offsetY); 145 setScaleX(scaleX); 146 setScaleY(scaleY); 147 } 148 149 /** 150 * The input for this {@code Effect}. 151 * If set to {@code null}, or left unspecified, a graphical image of 152 * the {@code Node} to which the {@code Effect} is attached will be 153 * used as the input. 154 * @defaultValue null 155 */ 156 private ObjectProperty<Effect> input; 157 158 159 public final void setInput(Effect value) { 160 inputProperty().set(value); 161 } 162 163 public final Effect getInput() { 164 return input == null ? null : input.get(); 165 } 166 167 public final ObjectProperty<Effect> inputProperty() { 168 if (input == null) { 169 input = new EffectInputProperty("input"); 170 } 171 return input; 172 } 173 174 @Override 175 boolean impl_checkChainContains(Effect e) { 176 Effect localInput = getInput(); 177 if (localInput == null) 178 return false; 179 if (localInput == e) 180 return true; 181 return localInput.impl_checkChainContains(e); 182 } 183 184 private final FloatMap defaultMap = new FloatMap(1, 1); 185 186 /** 187 * The map data for this {@code Effect}. 188 * @defaultValue an empty map 189 */ 190 private ObjectProperty<FloatMap> mapData; 191 192 193 public final void setMapData(FloatMap value) { 194 mapDataProperty().set(value); 195 } 196 197 public final FloatMap getMapData() { 198 return mapData == null ? null : mapData.get(); 199 } 200 201 public final ObjectProperty<FloatMap> mapDataProperty() { 202 if (mapData == null) { 203 mapData = new ObjectPropertyBase<FloatMap>() { 204 205 @Override 206 public void invalidated() { 207 markDirty(EffectDirtyBits.EFFECT_DIRTY); 208 effectBoundsChanged(); 209 } 210 211 @Override 212 public Object getBean() { 213 return DisplacementMap.this; 214 } 215 216 @Override 217 public String getName() { 218 return "mapData"; 219 } 220 }; 221 } 222 return mapData; 223 } 224 225 private final MapDataChangeListener mapDataChangeListener = new MapDataChangeListener(); 226 227 private class MapDataChangeListener extends EffectChangeListener { 228 FloatMap mapData; 229 230 public void register(FloatMap value) { 231 mapData = value; 232 super.register(mapData == null ? null : mapData.effectDirtyProperty()); 233 } 234 235 @Override 236 public void invalidated(Observable valueModel) { 237 if (mapData.impl_isEffectDirty()) { 238 markDirty(EffectDirtyBits.EFFECT_DIRTY); 239 effectBoundsChanged(); 240 } 241 } 242 }; 243 244 /** 245 * The scale factor by which all x coordinate offset values in the 246 * {@code FloatMap} are multiplied. 247 * <pre> 248 * Min: n/a 249 * Max: n/a 250 * Default: 1.0 251 * Identity: 1.0 252 * </pre> 253 * @defaultValue 1.0 254 */ 255 private DoubleProperty scaleX; 256 257 258 public final void setScaleX(double value) { 259 scaleXProperty().set(value); 260 } 261 262 public final double getScaleX() { 263 return scaleX == null ? 1 : scaleX.get(); 264 } 265 266 public final DoubleProperty scaleXProperty() { 267 if (scaleX == null) { 268 scaleX = new DoublePropertyBase(1) { 269 270 @Override 271 public void invalidated() { 272 markDirty(EffectDirtyBits.EFFECT_DIRTY); 273 } 274 275 @Override 276 public Object getBean() { 277 return DisplacementMap.this; 278 } 279 280 @Override 281 public String getName() { 282 return "scaleX"; 283 } 284 }; 285 } 286 return scaleX; 287 } 288 289 /** 290 * The scale factor by which all y coordinate offset values in the 291 * {@code FloatMap} are multiplied. 292 * <pre> 293 * Min: n/a 294 * Max: n/a 295 * Default: 1.0 296 * Identity: 1.0 297 * </pre> 298 * @defaultValue 1.0 299 */ 300 private DoubleProperty scaleY; 301 302 303 public final void setScaleY(double value) { 304 scaleYProperty().set(value); 305 } 306 307 public final double getScaleY() { 308 return scaleY == null ? 1 : scaleY.get(); 309 } 310 311 public final DoubleProperty scaleYProperty() { 312 if (scaleY == null) { 313 scaleY = new DoublePropertyBase(1) { 314 315 @Override 316 public void invalidated() { 317 markDirty(EffectDirtyBits.EFFECT_DIRTY); 318 } 319 320 @Override 321 public Object getBean() { 322 return DisplacementMap.this; 323 } 324 325 @Override 326 public String getName() { 327 return "scaleY"; 328 } 329 }; 330 } 331 return scaleY; 332 } 333 334 /** 335 * The offset by which all x coordinate offset values in the 336 * {@code FloatMap} are displaced after they are scaled. 337 * <pre> 338 * Min: n/a 339 * Max: n/a 340 * Default: 0.0 341 * Identity: 0.0 342 * </pre> 343 * @defaultValue 0.0 344 */ 345 private DoubleProperty offsetX; 346 347 348 public final void setOffsetX(double value) { 349 offsetXProperty().set(value); 350 } 351 352 public final double getOffsetX() { 353 return offsetX == null ? 0 : offsetX.get(); 354 } 355 356 public final DoubleProperty offsetXProperty() { 357 if (offsetX == null) { 358 offsetX = new DoublePropertyBase() { 359 360 @Override 361 public void invalidated() { 362 markDirty(EffectDirtyBits.EFFECT_DIRTY); 363 } 364 365 @Override 366 public Object getBean() { 367 return DisplacementMap.this; 368 } 369 370 @Override 371 public String getName() { 372 return "offsetX"; 373 } 374 }; 375 } 376 return offsetX; 377 } 378 379 /** 380 * The offset by which all y coordinate offset values in the 381 * {@code FloatMap} are displaced after they are scaled. 382 * <pre> 383 * Min: n/a 384 * Max: n/a 385 * Default: 0.0 386 * Identity: 0.0 387 * </pre> 388 * @defaultValue 0.0 389 */ 390 private DoubleProperty offsetY; 391 392 393 public final void setOffsetY(double value) { 394 offsetYProperty().set(value); 395 } 396 397 public final double getOffsetY() { 398 return offsetY == null ? 0 : offsetY.get(); 399 } 400 401 public final DoubleProperty offsetYProperty() { 402 if (offsetY == null) { 403 offsetY = new DoublePropertyBase() { 404 405 @Override 406 public void invalidated() { 407 markDirty(EffectDirtyBits.EFFECT_DIRTY); 408 } 409 410 @Override 411 public Object getBean() { 412 return DisplacementMap.this; 413 } 414 415 @Override 416 public String getName() { 417 return "offsetY"; 418 } 419 }; 420 } 421 return offsetY; 422 } 423 424 /** 425 * Defines whether values taken from outside the edges of the map 426 * "wrap around" or not. 427 * <pre> 428 * Min: n/a 429 * Max: n/a 430 * Default: false 431 * Identity: n/a 432 * </pre> 433 * @defaultValue false 434 */ 435 private BooleanProperty wrap; 436 437 438 public final void setWrap(boolean value) { 439 wrapProperty().set(value); 440 } 441 442 public final boolean isWrap() { 443 return wrap == null ? false : wrap.get(); 444 } 445 446 public final BooleanProperty wrapProperty() { 447 if (wrap == null) { 448 wrap = new BooleanPropertyBase() { 449 450 @Override 451 public void invalidated() { 452 markDirty(EffectDirtyBits.EFFECT_DIRTY); 453 } 454 455 @Override 456 public Object getBean() { 457 return DisplacementMap.this; 458 } 459 460 @Override 461 public String getName() { 462 return "wrap"; 463 } 464 }; 465 } 466 return wrap; 467 } 468 469 @Override 470 void impl_update() { 471 Effect localInput = getInput(); 472 if (localInput != null) { 473 localInput.impl_sync(); 474 } 475 476 com.sun.scenario.effect.DisplacementMap peer = 477 (com.sun.scenario.effect.DisplacementMap) impl_getImpl(); 478 peer.setContentInput(localInput == null ? null : localInput.impl_getImpl()); 479 FloatMap localMapData = getMapData(); 480 mapDataChangeListener.register(localMapData); 481 if (localMapData != null) { 482 localMapData.impl_sync(); 483 peer.setMapData(localMapData.getImpl()); 484 } else { 485 defaultMap.impl_sync(); 486 peer.setMapData(defaultMap.getImpl()); 487 } 488 peer.setScaleX((float)getScaleX()); 489 peer.setScaleY((float)getScaleY()); 490 peer.setOffsetX((float)getOffsetX()); 491 peer.setOffsetY((float)getOffsetY()); 492 peer.setWrap(isWrap()); 493 } 494 495 /** 496 * @treatAsPrivate implementation detail 497 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 498 */ 499 @Deprecated 500 @Override 501 public BaseBounds impl_getBounds(BaseBounds bounds, 502 BaseTransform tx, 503 Node node, 504 BoundsAccessor boundsAccessor) { 505 bounds = EffectUtils.getInputBounds(bounds, 506 BaseTransform.IDENTITY_TRANSFORM, 507 node, boundsAccessor, 508 getInput()); 509 return EffectUtils.transformBounds(tx, bounds); 510 } 511 512 /** 513 * 514 * @treatAsPrivate implementation detail 515 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 516 */ 517 @Deprecated 518 @Override 519 public Effect impl_copy() { 520 DisplacementMap dm = new DisplacementMap(this.getMapData().impl_copy(), 521 this.getOffsetX(), this.getOffsetY(), this.getScaleX(), 522 this.getScaleY()); 523 dm.setInput(this.getInput()); 524 return dm; 525 } 526}