All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.effect.DisplacementMap Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.scene.effect;

import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.BooleanPropertyBase;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.DoublePropertyBase;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.scene.Node;

import com.sun.javafx.effect.EffectDirtyBits;
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.scene.BoundsAccessor;

/**
 * An effect that shifts each pixel by a distance specified by
 * the first two bands of of the specified {@link FloatMap}.
 * For each pixel in the output, the corresponding data from the
 * {@code mapData} is retrieved, scaled and offset by the {@code scale}
 * and {@code offset} attributes, scaled again by the size of the
 * source input image and used as an offset from the destination pixel
 * to retrieve the pixel data from the source input.
 * 

* {@code dst[x, y] = src[(x, y) + (offset + scale * map[x, y]) * (srcw, srch)]} *

* A value of {@code (0.0, 0.0)} would specify no offset for the * pixel data whereas a value of {@code (0.5, 0.5)} would specify * an offset of half of the source image size. *

* Note that the mapping is the offset from a destination pixel to * the source pixel location from which it is sampled which means that * filling the map with all values of {@code 0.5} would displace the * image by half of its size towards the upper left since each destination * pixel would contain the data that comes from the source pixel below and * to the right of it. *

*

* Also note that this effect does not adjust the coordinates of input * events or any methods that measure containment on a {@code Node}. * The results of mouse picking and the containment methods are undefined * when a {@code Node} has a {@code DisplacementMap} effect in place. *

*

* Example: *

{@code  int width = 220;
 * int height = 100;
 *
 * FloatMap floatMap = new FloatMap();
 * floatMap.setWidth(width);
 * floatMap.setHeight(height);
 *
 * for (int i = 0; i < width; i++) {
 *     double v = (Math.sin(i / 20.0 * Math.PI) - 0.5) / 40.0;
 *     for (int j = 0; j < height; j++) {
 *         floatMap.setSamples(i, j, 0.0f, (float) v);
 *     }
 * }
 *
 * DisplacementMap displacementMap = new DisplacementMap();
 * displacementMap.setMapData(floatMap);
 *
 * Text text = new Text();
 * text.setX(40.0);
 * text.setY(80.0);
 * text.setText("Wavy Text");
 * text.setFill(Color.web("0x3b596d"));
 * text.setFont(Font.font(null, FontWeight.BOLD, 50));
 * text.setEffect(displacementMap);}
* *

The code above produces the following:

*

The visual effect of
 * DisplacementMap on text

* @since JavaFX 2.0 */ public class DisplacementMap extends Effect { @Override com.sun.scenario.effect.DisplacementMap createPeer() { return new com.sun.scenario.effect.DisplacementMap( new com.sun.scenario.effect.FloatMap(1, 1), com.sun.scenario.effect.Effect.DefaultInput); } /** * Creates a new instance of DisplacementMap with default parameters. */ public DisplacementMap() { setMapData(new FloatMap(1, 1)); } /** * Creates a new instance of DisplacementMap with the specified mapData. * @param mapData the map data for this displacement map effect * @since JavaFX 2.1 */ public DisplacementMap(FloatMap mapData) { setMapData(mapData); } /** * Creates a new instance of DisplacementMap with the specified mapData, * offsetX, offsetY, scaleX, and scaleY. * @param mapData the map data for this displacement map effect * @param offsetX the offset by which all x coordinate offset values in the * {@code FloatMap} are displaced after they are scaled * @param offsetY the offset by which all y coordinate offset values in the * {@code FloatMap} are displaced after they are scaled * @param scaleX the scale factor by which all x coordinate offset values in the * {@code FloatMap} are multiplied * @param scaleY the scale factor by which all y coordinate offset values in the * {@code FloatMap} are multiplied * @since JavaFX 2.1 */ public DisplacementMap(FloatMap mapData, double offsetX, double offsetY, double scaleX, double scaleY) { setMapData(mapData); setOffsetX(offsetX); setOffsetY(offsetY); setScaleX(scaleX); setScaleY(scaleY); } /** * The input for this {@code Effect}. * If set to {@code null}, or left unspecified, a graphical image of * the {@code Node} to which the {@code Effect} is attached will be * used as the input. * @defaultValue null */ private ObjectProperty input; public final void setInput(Effect value) { inputProperty().set(value); } public final Effect getInput() { return input == null ? null : input.get(); } public final ObjectProperty inputProperty() { if (input == null) { input = new EffectInputProperty("input"); } return input; } @Override boolean checkChainContains(Effect e) { Effect localInput = getInput(); if (localInput == null) return false; if (localInput == e) return true; return localInput.checkChainContains(e); } private final FloatMap defaultMap = new FloatMap(1, 1); /** * The map data for this {@code Effect}. * @defaultValue an empty map */ private ObjectProperty mapData; public final void setMapData(FloatMap value) { mapDataProperty().set(value); } public final FloatMap getMapData() { return mapData == null ? null : mapData.get(); } public final ObjectProperty mapDataProperty() { if (mapData == null) { mapData = new ObjectPropertyBase<>() { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); effectBoundsChanged(); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "mapData"; } }; } return mapData; } private final MapDataChangeListener mapDataChangeListener = new MapDataChangeListener(); private class MapDataChangeListener extends EffectChangeListener { FloatMap mapData; public void register(FloatMap value) { mapData = value; super.register(mapData == null ? null : mapData.effectDirtyProperty()); } @Override public void invalidated(Observable valueModel) { if (mapData.isEffectDirty()) { markDirty(EffectDirtyBits.EFFECT_DIRTY); effectBoundsChanged(); } } } /** * The scale factor by which all x coordinate offset values in the * {@code FloatMap} are multiplied. *
     *       Min: n/a
     *       Max: n/a
     *   Default: 1.0
     *  Identity: 1.0
     * 
* @defaultValue 1.0 */ private DoubleProperty scaleX; public final void setScaleX(double value) { scaleXProperty().set(value); } public final double getScaleX() { return scaleX == null ? 1 : scaleX.get(); } public final DoubleProperty scaleXProperty() { if (scaleX == null) { scaleX = new DoublePropertyBase(1) { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "scaleX"; } }; } return scaleX; } /** * The scale factor by which all y coordinate offset values in the * {@code FloatMap} are multiplied. *
     *       Min: n/a
     *       Max: n/a
     *   Default: 1.0
     *  Identity: 1.0
     * 
* @defaultValue 1.0 */ private DoubleProperty scaleY; public final void setScaleY(double value) { scaleYProperty().set(value); } public final double getScaleY() { return scaleY == null ? 1 : scaleY.get(); } public final DoubleProperty scaleYProperty() { if (scaleY == null) { scaleY = new DoublePropertyBase(1) { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "scaleY"; } }; } return scaleY; } /** * The offset by which all x coordinate offset values in the * {@code FloatMap} are displaced after they are scaled. *
     *       Min: n/a
     *       Max: n/a
     *   Default: 0.0
     *  Identity: 0.0
     * 
* @defaultValue 0.0 */ private DoubleProperty offsetX; public final void setOffsetX(double value) { offsetXProperty().set(value); } public final double getOffsetX() { return offsetX == null ? 0 : offsetX.get(); } public final DoubleProperty offsetXProperty() { if (offsetX == null) { offsetX = new DoublePropertyBase() { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "offsetX"; } }; } return offsetX; } /** * The offset by which all y coordinate offset values in the * {@code FloatMap} are displaced after they are scaled. *
     *       Min: n/a
     *       Max: n/a
     *   Default: 0.0
     *  Identity: 0.0
     * 
* @defaultValue 0.0 */ private DoubleProperty offsetY; public final void setOffsetY(double value) { offsetYProperty().set(value); } public final double getOffsetY() { return offsetY == null ? 0 : offsetY.get(); } public final DoubleProperty offsetYProperty() { if (offsetY == null) { offsetY = new DoublePropertyBase() { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "offsetY"; } }; } return offsetY; } /** * Defines whether values taken from outside the edges of the map * "wrap around" or not. *
     *       Min:  n/a
     *       Max:  n/a
     *   Default: false
     *  Identity:  n/a
     * 
* @defaultValue false */ private BooleanProperty wrap; public final void setWrap(boolean value) { wrapProperty().set(value); } public final boolean isWrap() { return wrap == null ? false : wrap.get(); } public final BooleanProperty wrapProperty() { if (wrap == null) { wrap = new BooleanPropertyBase() { @Override public void invalidated() { markDirty(EffectDirtyBits.EFFECT_DIRTY); } @Override public Object getBean() { return DisplacementMap.this; } @Override public String getName() { return "wrap"; } }; } return wrap; } @Override void update() { Effect localInput = getInput(); if (localInput != null) { localInput.sync(); } com.sun.scenario.effect.DisplacementMap peer = (com.sun.scenario.effect.DisplacementMap) getPeer(); peer.setContentInput(localInput == null ? null : localInput.getPeer()); FloatMap localMapData = getMapData(); mapDataChangeListener.register(localMapData); if (localMapData != null) { localMapData.sync(); peer.setMapData(localMapData.getImpl()); } else { defaultMap.sync(); peer.setMapData(defaultMap.getImpl()); } peer.setScaleX((float)getScaleX()); peer.setScaleY((float)getScaleY()); peer.setOffsetX((float)getOffsetX()); peer.setOffsetY((float)getOffsetY()); peer.setWrap(isWrap()); } @Override BaseBounds getBounds(BaseBounds bounds, BaseTransform tx, Node node, BoundsAccessor boundsAccessor) { bounds = getInputBounds(bounds, BaseTransform.IDENTITY_TRANSFORM, node, boundsAccessor, getInput()); return transformBounds(tx, bounds); } @Override Effect copy() { DisplacementMap dm = new DisplacementMap(this.getMapData().copy(), this.getOffsetX(), this.getOffsetY(), this.getScaleX(), this.getScaleY()); dm.setInput(this.getInput()); return dm; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy