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.property.DoubleProperty;
029import javafx.beans.property.DoublePropertyBase;
030import javafx.beans.property.ObjectProperty;
031import javafx.beans.property.ObjectPropertyBase;
032import javafx.scene.Node;
033import javafx.scene.paint.Color;
034
035import com.sun.javafx.Utils;
036import com.sun.javafx.effect.EffectDirtyBits;
037import com.sun.javafx.effect.EffectUtils;
038import com.sun.javafx.geom.BaseBounds;
039import com.sun.javafx.geom.transform.BaseTransform;
040import com.sun.javafx.scene.BoundsAccessor;
041import com.sun.javafx.tk.Toolkit;
042
043
044/**
045 * A high-level effect that renders a shadow inside the edges of the
046 * given content with the specified color, radius, and offset.
047 *
048 * <p>
049 * Example:
050 * <pre><code>
051 * InnerShadow innerShadow = new InnerShadow();
052 * innerShadow.setOffsetX(4);
053 * innerShadow.setOffsetY(4);
054 * innerShadow.setColor(Color.web("0x3b596d"));
055 *
056 * Text text = new Text();
057 * text.setEffect(innerShadow);
058 * text.setX(20);
059 * text.setY(100);
060 * text.setText("InnerShadow");
061 * text.setFill(Color.ALICEBLUE);
062 * text.setFont(Font.font(null, FontWeight.BOLD, 50));
063 * </pre></code>
064 * <p> The code above produces the following: </p>
065 * <p>
066 * <img * src="doc-files/innershadow.png"/>
067 * </p>
068 */
069public class InnerShadow extends Effect {
070    private boolean changeIsLocal;
071
072    /**
073     * Creates a new instance of InnerShadow with default parameters.
074     */
075    public InnerShadow() {}
076
077    /**
078     * Creates a new instance of InnerShadow with specified radius and color.
079     * @param radius the radius of the shadow blur kernel
080     * @param color the shadow {@code Color}
081     */
082    public InnerShadow(double radius, Color color) {
083        setRadius(radius);
084        setColor(color);
085    }
086
087    /**
088     * Creates a new instance of InnerShadow with specified radius, offsetX,
089     * offsetY and color.
090     * @param radius the radius of the shadow blur kernel
091     * @param offsetX the shadow offset in the x direction
092     * @param offsetY the shadow offset in the y direction
093     * @param color the shadow {@code Color}
094     */
095    public InnerShadow(double radius, double offsetX, double offsetY, Color color) {
096        setRadius(radius);
097        setOffsetX(offsetX);
098        setOffsetY(offsetY);
099        setColor(color);
100    }
101
102    /**
103     * Creates a new instance of InnerShadow with the specified blurType, color,
104     * radius, spread, offsetX and offsetY.
105     * @param blurType the algorithm used to blur the shadow
106     * @param color the shadow {@code Color}
107     * @param radius the radius of the shadow blur kernel
108     * @param choke the portion of the radius where the contribution of
109     * the source material will be 100%
110     * @param offsetX the shadow offset in the x direction
111     * @param offsetY the shadow offset in the y direction
112     */
113    public InnerShadow(BlurType blurType, Color color, double radius, double choke,
114            double offsetX, double offsetY) {
115        setBlurType(blurType);
116        setColor(color);
117        setRadius(radius);
118        setChoke(choke);
119        setOffsetX(offsetX);
120        setOffsetY(offsetY);
121    }
122
123    @Override
124    com.sun.scenario.effect.InnerShadow impl_createImpl() {
125        return new com.sun.scenario.effect.InnerShadow();
126    };
127
128    /**
129     * The input for this {@code Effect}.
130     * If set to {@code null}, or left unspecified, a graphical image of
131     * the {@code Node} to which the {@code Effect} is attached will be
132     * used as the input.
133     * @defaultValue null
134     * @since JavaFX 1.3
135     */
136    private ObjectProperty<Effect> input;
137
138
139    public final void setInput(Effect value) {
140        inputProperty().set(value);
141    }
142
143    public final Effect getInput() {
144        return input == null ? null : input.get();
145    }
146
147    public final ObjectProperty<Effect> inputProperty() {
148        if (input == null) {
149            input = new EffectInputProperty("input");
150        }
151        return input;
152    }
153
154    @Override
155    boolean impl_checkChainContains(Effect e) {
156        Effect localInput = getInput();
157        if (localInput == null)
158            return false;
159        if (localInput == e)
160            return true;
161        return localInput.impl_checkChainContains(e);
162    }
163
164    /**
165     * The radius of the shadow blur kernel.
166     * This attribute controls the distance that the shadow is spread
167     * to each side of the source pixels.
168     * Setting the radius is equivalent to setting both the {@code width}
169     * and {@code height} attributes to a value of {@code (2 * radius + 1)}.
170     * <pre>
171     *       Min:   0.0
172     *       Max: 127.0
173     *   Default:  10.0
174     *  Identity:   0.0
175     * </pre>
176     * @defaultValue 10.0
177     */
178    private DoubleProperty radius;
179
180
181    public final void setRadius(double value) {
182        radiusProperty().set(value);
183    }
184
185    public final double getRadius() {
186        return radius == null ? 10 : radius.get();
187    }
188
189    public final DoubleProperty radiusProperty() {
190        if (radius == null) {
191            radius = new DoublePropertyBase(10) {
192
193                @Override
194                public void invalidated() {
195                    // gettter here is necessary to make the property valid
196                    double localRadius = getRadius();
197                    if (!changeIsLocal) {
198                        changeIsLocal = true;
199                        updateRadius(localRadius);
200                        changeIsLocal = false;
201                        markDirty(EffectDirtyBits.EFFECT_DIRTY);
202                        effectBoundsChanged();
203                    }
204                }
205
206                @Override
207                public Object getBean() {
208                    return InnerShadow.this;
209                }
210
211                @Override
212                public String getName() {
213                    return "radius";
214                }
215            };
216        }
217        return radius;
218    }
219
220    private void updateRadius(double value) {
221        double newdim = (value * 2 + 1);
222        if (width != null && width.isBound()) {
223            if (height == null || !height.isBound()) {
224                setHeight(newdim * 2 - getWidth());
225            }
226        } else if (height != null && height.isBound()) {
227            setWidth(newdim * 2 - getHeight());
228        } else {
229            setWidth(newdim);
230            setHeight(newdim);
231        }
232    }
233
234    /**
235     * The horizontal size of the shadow blur kernel.
236     * This attribute controls the horizontal size of the total area over
237     * which the shadow of a single pixel is distributed by the blur algorithm.
238     * Values less than {@code 1.0} are not distributed beyond the original
239     * pixel and so have no blurring effect on the shadow.
240     * <pre>
241     *       Min:   0.0
242     *       Max: 255.0
243     *   Default:  21.0
244     *  Identity:  &lt;1.0
245     * </pre>
246     * @defaultValue 21.0
247     */
248    private DoubleProperty width;
249
250
251    public final void setWidth(double value) {
252        widthProperty().set(value);
253    }
254
255    public final double getWidth() {
256        return width == null ? 21 : width.get();
257    }
258
259    public final DoubleProperty widthProperty() {
260        if (width == null) {
261            width = new DoublePropertyBase(21) {
262
263                @Override
264                public void invalidated() {
265                    // gettter here is necessary to make the property valid
266                    double localWidth = getWidth();
267                    if (!changeIsLocal) {
268                        changeIsLocal = true;
269                        updateWidth(localWidth);
270                        changeIsLocal = false;
271                        markDirty(EffectDirtyBits.EFFECT_DIRTY);
272                        effectBoundsChanged();
273                    }
274                }
275
276                @Override
277                public Object getBean() {
278                    return InnerShadow.this;
279                }
280
281                @Override
282                public String getName() {
283                    return "width";
284                }
285            };
286        }
287        return width;
288    }
289
290    private void updateWidth(double value) {
291        if (radius == null || !radius.isBound()) {
292            double newrad = ((value + getHeight()) / 2);
293            newrad = ((newrad - 1) / 2);
294            if (newrad < 0) {
295                newrad = 0;
296            }
297            setRadius(newrad);
298        } else {
299            if (height == null || !height.isBound()) {
300                double newdim = (getRadius() * 2 + 1);
301                setHeight(newdim * 2 - value);
302            }
303        }
304    }
305
306    /**
307     * The vertical size of the shadow blur kernel.
308     * This attribute controls the vertical size of the total area over
309     * which the shadow of a single pixel is distributed by the blur algorithm.
310     * Values less than {@code 1.0} are not distributed beyond the original
311     * pixel and so have no blurring effect on the shadow.
312     * <pre>
313     *       Min:   0.0
314     *       Max: 255.0
315     *   Default:  21.0
316     *  Identity:  &lt;1.0
317     * </pre>
318     * @defaultValue 21.0
319     */
320    private DoubleProperty height;
321
322
323    public final void setHeight(double value) {
324        heightProperty().set(value);
325    }
326
327    public final double getHeight() {
328        return height == null ? 21 : height.get();
329    }
330
331    public final DoubleProperty heightProperty() {
332        if (height == null) {
333            height = new DoublePropertyBase(21) {
334
335                @Override
336                public void invalidated() {
337                    // gettter here is necessary to make the property valid
338                    double localHeight = getHeight();
339                    if (!changeIsLocal) {
340                        changeIsLocal = true;
341                        updateHeight(localHeight);
342                        changeIsLocal = false;
343                        markDirty(EffectDirtyBits.EFFECT_DIRTY);
344                        effectBoundsChanged();
345                    }
346                }
347
348                @Override
349                public Object getBean() {
350                    return InnerShadow.this;
351                }
352
353                @Override
354                public String getName() {
355                    return "height";
356                }
357            };
358        }
359        return height;
360    }
361    private void updateHeight(double value) {
362        if (radius == null || !radius.isBound()) {
363            double newrad = ((getWidth() + value) / 2);
364            newrad = ((newrad - 1) / 2);
365            if (newrad < 0) {
366                newrad = 0;
367            }
368            setRadius(newrad);
369        } else {
370            if (width == null || !width.isBound()) {
371                double newdim = (getRadius() * 2 + 1);
372                setWidth(newdim * 2 - value);
373            }
374        }
375    }
376
377    /**
378     * The algorithm used to blur the shadow.
379     * <pre>
380     *       Min: n/a
381     *       Max: n/a
382     *   Default: BlurType.THREE_PASS_BOX
383     *  Identity: n/a
384     * </pre>
385     * @defaultValue THREE_PASS_BOX
386     */
387    private ObjectProperty<BlurType> blurType;
388
389
390    public final void setBlurType(BlurType value) {
391        blurTypeProperty().set(value);
392    }
393
394    public final BlurType getBlurType() {
395        return blurType == null ? BlurType.THREE_PASS_BOX : blurType.get();
396    }
397
398    public final ObjectProperty<BlurType> blurTypeProperty() {
399        if (blurType == null) {
400            blurType = new ObjectPropertyBase<BlurType>(BlurType.THREE_PASS_BOX) {
401
402                @Override
403                public void invalidated() {
404                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
405                }
406
407                @Override
408                public Object getBean() {
409                    return InnerShadow.this;
410                }
411
412                @Override
413                public String getName() {
414                    return "blurType";
415                }
416            };
417        }
418        return blurType;
419    }
420
421    /**
422     * The choke of the shadow.
423     * The choke is the portion of the radius where the contribution of
424     * the source material will be 100%.
425     * The remaining portion of the radius will have a contribution
426     * controlled by the blur kernel.
427     * A choke of {@code 0.0} will result in a distribution of the
428     * shadow determined entirely by the blur algorithm.
429     * A choke of {@code 1.0} will result in a solid growth inward of the
430     * shadow from the edges to the limit of the radius with a very sharp
431     * cutoff to transparency inside the radius.
432     * <pre>
433     *       Min: 0.0
434     *       Max: 1.0
435     *   Default: 0.0
436     *  Identity: 0.0
437     * </pre>
438     * @defaultValue 0.0
439     */
440    private DoubleProperty choke;
441
442
443    public final void setChoke(double value) {
444        chokeProperty().set(value);
445    }
446
447    public final double getChoke() {
448        return choke == null ? 0 : choke.get();
449    }
450
451    public final DoubleProperty chokeProperty() {
452        if (choke == null) {
453            choke = new DoublePropertyBase() {
454
455                @Override
456                public void invalidated() {
457                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
458                }
459
460                @Override
461                public Object getBean() {
462                    return InnerShadow.this;
463                }
464
465                @Override
466                public String getName() {
467                    return "choke";
468                }
469            };
470        }
471        return choke;
472    }
473
474    /**
475     * The shadow {@code Color}.
476     * <pre>
477     *       Min: n/a
478     *       Max: n/a
479     *   Default: Color.BLACK
480     *  Identity: n/a
481     * </pre>
482     * @defaultValue BLACK
483     */
484    private ObjectProperty<Color> color;
485
486
487    public final void setColor(Color value) {
488        colorProperty().set(value);
489    }
490
491    public final Color getColor() {
492        return color == null ? Color.BLACK : color.get();
493    }
494
495    public final ObjectProperty<Color> colorProperty() {
496        if (color == null) {
497            color = new ObjectPropertyBase<Color>(Color.BLACK) {
498
499                @Override
500                public void invalidated() {
501                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
502                }
503
504                @Override
505                public Object getBean() {
506                    return InnerShadow.this;
507                }
508
509                @Override
510                public String getName() {
511                    return "color";
512                }
513            };
514        }
515        return color;
516    }
517
518    /**
519     * The shadow offset in the x direction, in pixels.
520     * <pre>
521     *       Min: n/a
522     *       Max: n/a
523     *   Default: 0.0
524     *  Identity: 0.0
525     * </pre>
526     * @defaultValue 0.0
527     */
528    private DoubleProperty offsetX;
529
530
531    public final void setOffsetX(double value) {
532        offsetXProperty().set(value);
533    }
534
535    public final double getOffsetX() {
536        return offsetX == null ? 0 : offsetX.get();
537    }
538
539    public final DoubleProperty offsetXProperty() {
540        if (offsetX == null) {
541            offsetX = new DoublePropertyBase() {
542
543                @Override
544                public void invalidated() {
545                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
546                    effectBoundsChanged();
547                }
548
549                @Override
550                public Object getBean() {
551                    return InnerShadow.this;
552                }
553
554                @Override
555                public String getName() {
556                    return "offsetX";
557                }
558            };
559        }
560        return offsetX;
561    }
562
563    /**
564     * The shadow offset in the y direction, in pixels.
565     * <pre>
566     *       Min: n/a
567     *       Max: n/a
568     *   Default: 0.0
569     *  Identity: 0.0
570     * </pre>
571     * @defaultValue 0.0
572     */
573    private DoubleProperty offsetY;
574
575
576    public final void setOffsetY(double value) {
577        offsetYProperty().set(value);
578    }
579
580    public final double getOffsetY() {
581        return offsetY == null ? 0 : offsetY.get();
582    }
583
584    public final DoubleProperty offsetYProperty() {
585        if (offsetY == null) {
586            offsetY = new DoublePropertyBase() {
587
588                @Override
589                public void invalidated() {
590                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
591                    effectBoundsChanged();
592                }
593
594                @Override
595                public Object getBean() {
596                    return InnerShadow.this;
597                }
598
599                @Override
600                public String getName() {
601                    return "offsetY";
602                }
603            };
604        }
605        return offsetY;
606    }
607
608    private Color getColorInternal() {
609        Color c = getColor();
610        return c == null ? Color.BLACK : c;
611    }
612
613    private BlurType getBlurTypeInternal() {
614        BlurType bt = getBlurType();
615        return bt == null ? BlurType.THREE_PASS_BOX : bt;
616    }
617
618    @Override
619    void impl_update() {
620        Effect localInput = getInput();
621        if (localInput != null) {
622            localInput.impl_sync();
623        }
624
625        com.sun.scenario.effect.InnerShadow peer =
626                (com.sun.scenario.effect.InnerShadow) impl_getImpl();
627        peer.setShadowSourceInput(localInput == null ? null : localInput.impl_getImpl());
628        peer.setContentInput(localInput == null ? null : localInput.impl_getImpl());
629        peer.setGaussianWidth((float)Utils.clamp(0, getWidth(), 255));
630        peer.setGaussianHeight((float)Utils.clamp(0, getHeight(), 255));
631        peer.setShadowMode(Toolkit.getToolkit().toShadowMode(getBlurTypeInternal()));
632        peer.setColor(Toolkit.getToolkit().toColor4f(getColorInternal()));
633        peer.setChoke((float)Utils.clamp(0, getChoke(), 1));
634        peer.setOffsetX((int) getOffsetX());
635        peer.setOffsetY((int) getOffsetY());
636    }
637
638    /**
639     * @treatAsPrivate implementation detail
640     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
641     */
642    @Deprecated
643    @Override
644    public BaseBounds impl_getBounds(BaseBounds bounds,
645                                     BaseTransform tx,
646                                     Node node,
647                                     BoundsAccessor boundsAccessor) {
648        return EffectUtils.getInputBounds(bounds, tx,
649                                          node, boundsAccessor,
650                                          getInput());
651    }
652
653    /**
654     * 
655     * @treatAsPrivate implementation detail
656     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
657     */
658    @Deprecated
659    @Override
660    public Effect impl_copy() {
661        InnerShadow is = new InnerShadow(this.getBlurType(), this.getColor(), 
662                this.getRadius(), this.getChoke(), this.getOffsetX(), 
663                this.getOffsetY());
664        is.setInput(this.getInput());
665        is.setWidth(this.getWidth());
666        is.setHeight(this.getHeight());
667        return is;
668    }
669}