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.scene.Node;
032
033import com.sun.javafx.effect.EffectDirtyBits;
034import com.sun.javafx.geom.BaseBounds;
035import com.sun.javafx.geom.RectBounds;
036import com.sun.javafx.geom.transform.BaseTransform;
037import com.sun.javafx.scene.BoundsAccessor;
038
039
040/**
041 * An effect that provides non-affine transformation of the input content.
042 * Most typically {@code PerspectiveTransform} is used to provide a "faux"
043 * three-dimensional effect for otherwise two-dimensional content.
044 * <p>
045 * A perspective transformation is capable of mapping an arbitrary
046 * quadrilateral into another arbitrary quadrilateral, while preserving
047 * the straightness of lines.  Unlike an affine transformation, the
048 * parallelism of lines in the source is not necessarily preserved in the
049 * output.
050 * <p>
051 * Note that this effect does not adjust the coordinates of input events
052 * or any methods that measure containment on a {@code Node}.
053 * The results of mouse picking and the containment methods are undefined
054 * when a {@code Node} has a {@code PerspectiveTransform} effect in place.
055 *
056 * <p>
057 * Example:
058 * <pre><code>
059 * PerspectiveTransform perspectiveTrasform = new PerspectiveTransform();
060 * perspectiveTrasform.setUlx(10.0);
061 * perspectiveTrasform.setUly(10.0);
062 * perspectiveTrasform.setUrx(310.0);
063 * perspectiveTrasform.setUry(40.0);
064 * perspectiveTrasform.setLrx(310.0);
065 * perspectiveTrasform.setLry(60.0);
066 * perspectiveTrasform.setLlx(10.0);
067 * perspectiveTrasform.setLly(90.0);
068 *
069 * Group g = new Group();
070 * g.setEffect(perspectiveTrasform);
071 * g.setCache(true);
072 *
073 * Rectangle rect = new Rectangle();
074 * rect.setX(10.0);
075 * rect.setY(10.0);
076 * rect.setWidth(280.0);
077 * rect.setHeight(80.0);
078 * rect.setFill(Color.web("0x3b596d"));
079 *
080 * Text text = new Text();
081 * text.setX(20.0);
082 * text.setY(65.0);
083 * text.setText("Perspective");
084 * text.setFill(Color.ALICEBLUE);
085 * text.setFont(Font.font(null, FontWeight.BOLD, 36));
086 *
087 * g.getChildren().addAll(rect, text);
088 * </pre></code>
089 * <p> The code above produces the following: </p>
090 * <p>
091 * <img src="doc-files/perspectivetransform.png"/>
092 * </p>
093 */
094public class PerspectiveTransform extends Effect {
095    /**
096     * Creates a new instance of PerspectiveTransform with default parameters.
097     */
098    public PerspectiveTransform() {}
099
100    /**
101     * Creates a new instance of PerspectiveTransform with the specified ulx,
102     * uly, urx, ury, lrx, lry, llx, and lly.
103     * @param ulx the x coordinate of upper left corner
104     * @param uly the y coordinate of upper left corner
105     * @param urx the x coordinate of upper right corner
106     * @param ury the y coordinate of upper right corner
107     * @param lrx the x coordinate of lower right corner
108     * @param lry the y coordinate of lower right corner
109     * @param llx the x coordinate of lower left corner
110     * @param lly the y coordinate of lower left corner
111     */
112    public PerspectiveTransform(double ulx, double uly,
113                                double urx, double ury,
114                                double lrx, double lry,
115                                double llx, double lly) {
116        setUlx(ulx); setUly(uly);
117        setUrx(urx); setUry(ury);
118        setLlx(llx); setLly(lly);
119        setLrx(lrx); setLry(lry);
120    }
121
122    private void updateXform() {
123        ((com.sun.scenario.effect.PerspectiveTransform)impl_getImpl()).setQuadMapping(
124                             (float)getUlx(), (float)getUly(),
125                             (float)getUrx(), (float)getUry(),
126                             (float)getLrx(), (float)getLry(),
127                             (float)getLlx(), (float)getLly());
128    }
129
130    @Override
131    com.sun.scenario.effect.PerspectiveTransform impl_createImpl() {
132        return new com.sun.scenario.effect.PerspectiveTransform();
133    };
134    /**
135     * The input for this {@code Effect}.
136     * If set to {@code null}, or left unspecified, a graphical image of
137     * the {@code Node} to which the {@code Effect} is attached will be
138     * used as the input.
139     * @defaultValue null
140     */
141    private ObjectProperty<Effect> input;
142
143
144    public final void setInput(Effect value) {
145        inputProperty().set(value);
146    }
147
148    public final Effect getInput() {
149        return input == null ? null : input.get();
150    }
151
152    public final ObjectProperty<Effect> inputProperty() {
153        if (input == null) {
154            input = new EffectInputProperty("input");
155        }
156        return input;
157    }
158
159    @Override
160    boolean impl_checkChainContains(Effect e) {
161        Effect localInput = getInput();
162        if (localInput == null)
163            return false;
164        if (localInput == e)
165            return true;
166        return localInput.impl_checkChainContains(e);
167    }
168
169    /**
170     * The x coordinate of the output location onto which the upper left
171     * corner of the source is mapped.
172     * @defaultValue 0.0
173     */
174    private DoubleProperty ulx;
175
176
177    public final void setUlx(double value) {
178        ulxProperty().set(value);
179    }
180
181    public final double getUlx() {
182        return ulx == null ? 0 : ulx.get();
183    }
184
185    public final DoubleProperty ulxProperty() {
186        if (ulx == null) {
187            ulx = new DoublePropertyBase() {
188
189                @Override
190                public void invalidated() {
191                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
192                    effectBoundsChanged();
193                }
194
195                @Override
196                public Object getBean() {
197                    return PerspectiveTransform.this;
198                }
199
200                @Override
201                public String getName() {
202                    return "ulx";
203                }
204            };
205        }
206        return ulx;
207    }
208
209    /**
210     * The y coordinate of the output location onto which the upper left
211     * corner of the source is mapped.
212     * @defaultValue 0.0
213     */
214    private DoubleProperty uly;
215
216
217    public final void setUly(double value) {
218        ulyProperty().set(value);
219    }
220
221    public final double getUly() {
222        return uly == null ? 0 : uly.get();
223    }
224
225    public final DoubleProperty ulyProperty() {
226        if (uly == null) {
227            uly = new DoublePropertyBase() {
228
229                @Override
230                public void invalidated() {
231                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
232                    effectBoundsChanged();
233                }
234
235                @Override
236                public Object getBean() {
237                    return PerspectiveTransform.this;
238                }
239
240                @Override
241                public String getName() {
242                    return "uly";
243                }
244            };
245        }
246        return uly;
247    }
248
249    /**
250     * The x coordinate of the output location onto which the upper right
251     * corner of the source is mapped.
252     * @defaultValue 0.0
253     */
254    private DoubleProperty urx;
255
256
257    public final void setUrx(double value) {
258        urxProperty().set(value);
259    }
260
261    public final double getUrx() {
262        return urx == null ? 0 : urx.get();
263    }
264
265    public final DoubleProperty urxProperty() {
266        if (urx == null) {
267            urx = new DoublePropertyBase() {
268
269                @Override
270                public void invalidated() {
271                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
272                    effectBoundsChanged();
273                }
274
275                @Override
276                public Object getBean() {
277                    return PerspectiveTransform.this;
278                }
279
280                @Override
281                public String getName() {
282                    return "urx";
283                }
284            };
285        }
286        return urx;
287    }
288
289    /**
290     * The y coordinate of the output location onto which the upper right
291     * corner of the source is mapped.
292     * @defaultValue 0.0
293     */
294    private DoubleProperty ury;
295
296
297    public final void setUry(double value) {
298        uryProperty().set(value);
299    }
300
301    public final double getUry() {
302        return ury == null ? 0 : ury.get();
303    }
304
305    public final DoubleProperty uryProperty() {
306        if (ury == null) {
307            ury = new DoublePropertyBase() {
308
309                @Override
310                public void invalidated() {
311                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
312                    effectBoundsChanged();
313                }
314
315                @Override
316                public Object getBean() {
317                    return PerspectiveTransform.this;
318                }
319
320                @Override
321                public String getName() {
322                    return "ury";
323                }
324            };
325        }
326        return ury;
327    }
328
329    /**
330     * The x coordinate of the output location onto which the lower right
331     * corner of the source is mapped.
332     * @defaultValue 0.0
333     */
334    private DoubleProperty lrx;
335
336
337    public final void setLrx(double value) {
338        lrxProperty().set(value);
339    }
340
341    public final double getLrx() {
342        return lrx == null ? 0 : lrx.get();
343    }
344
345    public final DoubleProperty lrxProperty() {
346        if (lrx == null) {
347            lrx = new DoublePropertyBase() {
348
349                @Override
350                public void invalidated() {
351                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
352                    effectBoundsChanged();
353                }
354
355                @Override
356                public Object getBean() {
357                    return PerspectiveTransform.this;
358                }
359
360                @Override
361                public String getName() {
362                    return "lrx";
363                }
364            };
365        }
366        return lrx;
367    }
368
369    /**
370     * The y coordinate of the output location onto which the lower right
371     * corner of the source is mapped.
372     * @defaultValue 0.0
373     */
374    private DoubleProperty lry;
375
376
377    public final void setLry(double value) {
378        lryProperty().set(value);
379    }
380
381    public final double getLry() {
382        return lry == null ? 0 : lry.get();
383    }
384
385    public final DoubleProperty lryProperty() {
386        if (lry == null) {
387            lry = new DoublePropertyBase() {
388
389                @Override
390                public void invalidated() {
391                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
392                    effectBoundsChanged();
393                }
394
395                @Override
396                public Object getBean() {
397                    return PerspectiveTransform.this;
398                }
399
400                @Override
401                public String getName() {
402                    return "lry";
403                }
404            };
405        }
406        return lry;
407    }
408
409    /**
410     * The x coordinate of the output location onto which the lower left
411     * corner of the source is mapped.
412     * @defaultValue 0.0
413     */
414    private DoubleProperty llx;
415
416
417    public final void setLlx(double value) {
418        llxProperty().set(value);
419    }
420
421    public final double getLlx() {
422        return llx == null ? 0 : llx.get();
423    }
424
425    public final DoubleProperty llxProperty() {
426        if (llx == null) {
427            llx = new DoublePropertyBase() {
428
429                @Override
430                public void invalidated() {
431                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
432                    effectBoundsChanged();
433                }
434
435                @Override
436                public Object getBean() {
437                    return PerspectiveTransform.this;
438                }
439
440                @Override
441                public String getName() {
442                    return "llx";
443                }
444            };
445        }
446        return llx;
447    }
448
449    /**
450     * The y coordinate of the output location onto which the lower left
451     * corner of the source is mapped.
452     * @defaultValue 0.0
453     */
454    private DoubleProperty lly;
455
456
457    public final void setLly(double value) {
458        llyProperty().set(value);
459    }
460
461    public final double getLly() {
462        return lly == null ? 0 : lly.get();
463    }
464
465    public final DoubleProperty llyProperty() {
466        if (lly == null) {
467            lly = new DoublePropertyBase() {
468
469                @Override
470                public void invalidated() {
471                    markDirty(EffectDirtyBits.EFFECT_DIRTY);
472                    effectBoundsChanged();
473                }
474
475                @Override
476                public Object getBean() {
477                    return PerspectiveTransform.this;
478                }
479
480                @Override
481                public String getName() {
482                    return "lly";
483                }
484            };
485        }
486        return lly;
487    }
488
489    @Override
490    void impl_update() {
491        Effect localInput = getInput();
492        if (localInput != null) {
493            localInput.impl_sync();
494        }
495
496        ((com.sun.scenario.effect.PerspectiveTransform)impl_getImpl())
497            .setInput(localInput == null ? null : localInput.impl_getImpl());
498        updateXform();
499    }
500
501    private float devcoords[] = new float[8];
502
503    /**
504     * @treatAsPrivate implementation detail
505     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
506     */
507    @Deprecated
508    @Override
509    public BaseBounds impl_getBounds(BaseBounds bounds,
510                                     BaseTransform tx,
511                                     Node node,
512                                     BoundsAccessor boundsAccessor) {
513        setupDevCoords(tx);
514
515        float minx, miny, maxx, maxy;
516        minx = maxx = devcoords[0];
517        miny = maxy = devcoords[1];
518        for (int i = 2; i < devcoords.length; i += 2) {
519            if (minx > devcoords[i]) minx = devcoords[i];
520            else if (maxx < devcoords[i]) maxx = devcoords[i];
521            if (miny > devcoords[i+1]) miny = devcoords[i+1];
522            else if (maxy < devcoords[i+1]) maxy = devcoords[i+1];
523        }
524
525        return new RectBounds(minx, miny, maxx, maxy);
526    }
527
528    private void setupDevCoords(BaseTransform transform) {
529        devcoords[0] = (float)getUlx();
530        devcoords[1] = (float)getUly();
531        devcoords[2] = (float)getUrx();
532        devcoords[3] = (float)getUry();
533        devcoords[4] = (float)getLrx();
534        devcoords[5] = (float)getLry();
535        devcoords[6] = (float)getLlx();
536        devcoords[7] = (float)getLly();
537        transform.transform(devcoords, 0, devcoords, 0, 4);
538    }
539
540    /**
541     * 
542     * @treatAsPrivate implementation detail
543     * @deprecated This is an internal API that is not intended for use and will be removed in the next version
544     */
545    @Deprecated
546    @Override
547    public Effect impl_copy() {
548        return new PerspectiveTransform(this.getUlx(), this.getUly(), 
549                this.getUrx(), this.getUry(), this.getLrx(), this.getLry(), 
550                this.getLlx(), this.getLly());
551    }
552}