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.IntegerProperty;
030import javafx.beans.property.ObjectPropertyBase;
031import javafx.beans.property.SimpleIntegerProperty;
032import javafx.scene.Node;
033
034import com.sun.javafx.effect.EffectDirtyBits;
035import com.sun.javafx.geom.BaseBounds;
036import com.sun.javafx.geom.transform.BaseTransform;
037import com.sun.javafx.scene.BoundsAccessor;
038
039/**
040 * The abstract base class for all effect implementations.
041 * An effect is a graphical algorithm that produces an image, typically
042 * as a modification of a source image.
043 * An effect can be associated with a scene graph {@code Node} by setting
044 * the {@link javafx.scene.Node#effectProperty Node.effect} attribute.
045 * Some effects change the color properties of the source pixels
046 * (such as {@link ColorAdjust}),
047 * others combine multiple images together (such as {@link Blend}),
048 * while still others warp or move the pixels of the source image around
049 * (such as {@link DisplacementMap} or {@link PerspectiveTransform}).
050 * All effects have at least one input defined and the input can be set
051 * to another effect to chain the effects together and combine their
052 * results, or it can be left unspecified in which case the effect will
053 * operate on a graphical rendering of the node it is attached to.
054 * <p>
055 * Note: this is a conditional feature. See
056 * {@link javafx.application.ConditionalFeature#EFFECT ConditionalFeature.EFFECT}
057 * for more information.
058 */
059public abstract class Effect {
060    /**
061     * Creates a new Effect.
062     */
063    protected Effect() {
064       markDirty(EffectDirtyBits.EFFECT_DIRTY);
065    }
066
067    void effectBoundsChanged() {
068        toggleDirty(EffectDirtyBits.BOUNDS_CHANGED);
069    }
070
071     private com.sun.scenario.effect.Effect peer;
072     abstract com.sun.scenario.effect.Effect impl_createImpl();
073
074    /**
075     * @treatAsPrivate implementation detail
076     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
077     */
078    @Deprecated
079    public com.sun.scenario.effect.Effect impl_getImpl() {
080        if (peer == null) {
081            peer = impl_createImpl();
082        }
083        return peer;
084    }
085
086    // effect is marked dirty in the constructor, so we don't need to be lazy here
087    private IntegerProperty effectDirty =
088            new SimpleIntegerProperty(this, "effectDirty");
089
090    private void setEffectDirty(int value) {
091        impl_effectDirtyProperty().set(value);
092    }
093
094    /**
095     * @treatAsPrivate implementation detail
096     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
097     */
098    @Deprecated
099    public final IntegerProperty impl_effectDirtyProperty() {
100        return effectDirty;
101    }
102
103    /**
104     * @treatAsPrivate implementation detail
105     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
106     */
107    @Deprecated
108    public final boolean impl_isEffectDirty() {
109        return isEffectDirty(EffectDirtyBits.EFFECT_DIRTY);
110    }
111
112    /**
113     * Set the specified dirty bit
114     */
115    final void markDirty(EffectDirtyBits dirtyBit) {
116        setEffectDirty(effectDirty.get() | dirtyBit.getMask());
117    }
118
119    /**
120     * Toggle the specified dirty bit
121     */
122    private void toggleDirty(EffectDirtyBits dirtyBit) {
123        setEffectDirty(effectDirty.get() ^ dirtyBit.getMask());
124    }
125
126    /**
127     * Test the specified dirty bit
128     */
129    private boolean isEffectDirty(EffectDirtyBits dirtyBit) {
130        return ((effectDirty.get() & dirtyBit.getMask()) != 0);
131    }
132
133    /**
134     * Clear the specified dirty bit
135     */
136    private void clearEffectDirty(EffectDirtyBits dirtyBit) {
137        setEffectDirty(effectDirty.get() & ~dirtyBit.getMask());
138    }
139
140    /**
141     * @treatAsPrivate implementation detail
142     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
143     */
144    @Deprecated
145    public final void impl_sync() {
146        if (isEffectDirty(EffectDirtyBits.EFFECT_DIRTY)) {
147            impl_update();
148            clearEffectDirty(EffectDirtyBits.EFFECT_DIRTY);
149        }
150    }
151
152    abstract void impl_update();
153
154    abstract boolean impl_checkChainContains(Effect e);
155
156    boolean impl_containsCycles(Effect value) {
157        if (value != null
158                && (value == this || value.impl_checkChainContains(this))) {
159            return true;
160        }
161        return false;
162    }
163
164    class EffectInputChangeListener extends EffectChangeListener {
165        private int oldBits;
166
167        public void register(Effect value) {
168            super.register(value == null? null: value.impl_effectDirtyProperty());
169            if (value != null) {
170                oldBits = value.impl_effectDirtyProperty().get();
171            }
172        }
173
174        @Override
175        public void invalidated(Observable valueModel) {
176            int newBits = ((IntegerProperty)valueModel).get();
177            int dirtyBits = newBits ^ oldBits;
178            oldBits = newBits;
179            if (EffectDirtyBits.isSet(dirtyBits, EffectDirtyBits.EFFECT_DIRTY)
180                && EffectDirtyBits.isSet(newBits, EffectDirtyBits.EFFECT_DIRTY)) {
181                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
182            }
183            if (EffectDirtyBits.isSet(dirtyBits, EffectDirtyBits.BOUNDS_CHANGED)) {
184                toggleDirty(EffectDirtyBits.BOUNDS_CHANGED);
185            }
186        }
187    }
188
189    class EffectInputProperty extends ObjectPropertyBase<Effect> {
190        private final String propertyName;
191
192        private Effect validInput = null;
193        
194        private final EffectInputChangeListener effectChangeListener =
195                new EffectInputChangeListener();
196
197        public EffectInputProperty(final String propertyName) {
198            this.propertyName = propertyName;
199        }
200
201        @Override
202        public void invalidated() {
203            final Effect newInput = super.get();
204            if (impl_containsCycles(newInput)) {
205                if (isBound()) {
206                    unbind();
207                    set(validInput);
208                    throw new IllegalArgumentException("Cycle in effect chain "
209                            + "detected, binding was set to incorrect value, "
210                            + "unbinding the input property");
211                } else {
212                    set(validInput);
213                    throw new IllegalArgumentException("Cycle in effect chain detected");
214                }
215            }
216            validInput = newInput;
217            effectChangeListener.register(newInput);
218            markDirty(EffectDirtyBits.EFFECT_DIRTY);
219
220            // we toggle dirty flag for bounds on this effect to notify
221            // "consumers" of this effect that bounds have changed
222            //
223            // bounds of this effect might change
224            // even if bounds of chained effect are not dirty
225            effectBoundsChanged();
226        }
227
228        @Override
229        public Object getBean() {
230            return Effect.this;
231        }
232
233        @Override
234        public String getName() {
235            return propertyName;
236        }
237    }
238
239   /**
240    * Returns bounds of given node with applied effect.
241    *
242    * We *never* pass null in as a bounds. This method will
243    * NOT take a null bounds object. The returned value may be
244    * the same bounds object passed in, or it may be a new object.
245    *
246    * @treatAsPrivate implementation detail
247    * @deprecated This is an internal API that is not intended for use and will be removed in the next version
248    */
249    @Deprecated
250    public abstract BaseBounds impl_getBounds(BaseBounds bounds,
251                                              BaseTransform tx,
252                                              Node node,
253                                              BoundsAccessor boundsAccessor);
254    /**
255     * 
256     * @treatAsPrivate implementation detail
257     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
258     */
259    @Deprecated
260    public abstract Effect impl_copy();
261    
262}