modules/graphics/src/main/java/com/sun/scenario/effect/FilterEffect.java

Print this page




  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.scenario.effect;
  27 
  28 import com.sun.javafx.geom.Point2D;
  29 import com.sun.javafx.geom.BaseBounds;
  30 import com.sun.javafx.geom.RectBounds;
  31 import com.sun.javafx.geom.Rectangle;
  32 import com.sun.javafx.geom.transform.BaseTransform;
  33 import com.sun.javafx.geom.transform.NoninvertibleTransformException;

  34 
  35 /**
  36  * The implementation base class for {@link Effect} subclasses that operate
  37  * by filtering the inputs at the pixel level.
  38  */
  39 public abstract class FilterEffect extends Effect {
  40     protected FilterEffect() {
  41         super();
  42     }
  43 
  44     protected FilterEffect(Effect input) {
  45         super(input);
  46     }
  47 
  48     protected FilterEffect(Effect input1, Effect input2) {
  49         super(input1, input2);
  50     }
  51 
  52     public boolean operatesInUserSpace() {
  53         return false;
  54     }
  55 
  56     public BaseBounds getBounds(BaseTransform transform,
  57                               Effect defaultInput)
  58     {
  59         int numinputs = getNumInputs();
  60         BaseTransform inputtx = transform;
  61         if (operatesInUserSpace()) {
  62             inputtx = BaseTransform.IDENTITY_TRANSFORM;
  63         }
  64         BaseBounds ret;
  65         if (numinputs == 1) {
  66             Effect input = getDefaultedInput(0, defaultInput);
  67             ret = input.getBounds(inputtx, defaultInput);
  68         } else {
  69             BaseBounds inputBounds[] = new BaseBounds[numinputs];
  70             for (int i = 0; i < numinputs; i++) {
  71                 Effect input = getDefaultedInput(i, defaultInput);
  72                 inputBounds[i] = input.getBounds(inputtx, defaultInput);
  73             }
  74             ret = combineBounds(inputBounds);
  75         }
  76         if (inputtx != transform) {
  77             ret = transformBounds(transform, ret);
  78         }
  79         return ret;
  80     }
  81 
  82     protected abstract Rectangle getInputClip(int inputIndex,
  83                                               BaseTransform transform,
  84                                               Rectangle outputBounds);
  85 
  86     protected static Rectangle untransformClip(BaseTransform transform,
  87                                                Rectangle clip)
  88     {
  89         if (transform.isIdentity() || clip == null || clip.isEmpty()) {
  90             return clip;
  91         }
  92         // We are asked to produce samples for the pixels in the
  93         // Rectangular clip.  The samples requested are delivered for
  94         // the centers of the pixels for every pixel in that range.
  95         // Thus, we need valid data for the clip inset by 0.5 pixels
  96         // all around.
  97         // But, when we untransform, we need to make sure that the data
  98         // we provide can be used to provide a valid sample for each of
  99         // those points.  If the mapped sample coordinate falls on a
 100         // non-integer coordinate then we need the data for the 4 pixels
 101         // around that point.  Thus, we need a sample for the pixel that it
 102         // falls on, and potentially a sample for the next pixel over if
 103         // we are within 0.5 pixels of the edge of those border pixels.
 104         // The full operation is then:
 105         //     clip.inset(0.5)       // reduce to requested pixel centers


 127             }
 128             return transformedBounds;
 129         }
 130         RectBounds b = new RectBounds(clip);
 131         try {
 132             b.grow(-0.5f, -0.5f);
 133             b = (RectBounds) transform.inverseTransform(b, b);
 134             b.grow(0.5f, 0.5f);
 135             transformedBounds.setBounds(b);
 136         } catch (NoninvertibleTransformException e) {
 137             // Non-invertible means the transform has collapsed onto
 138             // a point or line and so the results of the effect are
 139             // not visible so we can use the empty bounds object we
 140             // created for transformedBounds.  Ideally this would be
 141             // checked further up in the chain, but in case we get here
 142             // we might as well do as little work as we can.
 143         }
 144         return transformedBounds;
 145     }
 146 
























 147     @Override
 148     public ImageData filter(FilterContext fctx,
 149                             BaseTransform transform,
 150                             Rectangle outputClip,
 151                             Object renderHelper,
 152                             Effect defaultInput)
 153     {


 154         int numinputs = getNumInputs();
 155         ImageData inputDatas[] = new ImageData[numinputs];
 156         Rectangle inputClip;
 157         BaseTransform inputtx;
 158         if (operatesInUserSpace()) {
 159             inputClip = untransformClip(transform, outputClip);
 160             inputtx = BaseTransform.IDENTITY_TRANSFORM;
 161         } else {
 162             inputClip = outputClip;
 163             inputtx = transform;
 164         }
 165         for (int i = 0; i < numinputs; i++) {
 166             Effect input = getDefaultedInput(i, defaultInput);
 167             inputDatas[i] =
 168                 input.filter(fctx, inputtx,
 169                              getInputClip(i, inputtx, inputClip),
 170                              null, defaultInput);
 171             if (!inputDatas[i].validate(fctx)) {
 172                 for (int j = 0; j <= i; j++) {
 173                     inputDatas[j].unref();
 174                 }
 175                 return new ImageData(fctx, null, null);
 176             }
 177         }
 178         ImageData ret = filterImageDatas(fctx, inputtx, inputClip, inputDatas);
 179         for (int i = 0; i < numinputs; i++) {
 180             inputDatas[i].unref();
 181         }
 182         if (inputtx != transform) {
 183             if (renderHelper instanceof ImageDataRenderer) {
 184                 ImageDataRenderer renderer = (ImageDataRenderer) renderHelper;
 185                 renderer.renderImage(ret, transform, fctx);
 186                 ret.unref();
 187                 ret = null;
 188             } else {
 189                 ret = ret.transform(transform);
 190             }
 191         }
 192         return ret;
 193     }
 194 
 195     @Override
 196     public Point2D transform(Point2D p, Effect defaultInput) {
 197         return getDefaultedInput(0, defaultInput).transform(p, defaultInput);
 198     }
 199 
 200     @Override
 201     public Point2D untransform(Point2D p, Effect defaultInput) {
 202         return getDefaultedInput(0, defaultInput).untransform(p, defaultInput);
 203     }
 204 
 205     protected abstract ImageData filterImageDatas(FilterContext fctx,
 206                                                   BaseTransform transform,
 207                                                   Rectangle outputClip,

 208                                                   ImageData... inputDatas);
 209 }


  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.scenario.effect;
  27 
  28 import com.sun.javafx.geom.Point2D;
  29 import com.sun.javafx.geom.BaseBounds;
  30 import com.sun.javafx.geom.RectBounds;
  31 import com.sun.javafx.geom.Rectangle;
  32 import com.sun.javafx.geom.transform.BaseTransform;
  33 import com.sun.javafx.geom.transform.NoninvertibleTransformException;
  34 import com.sun.scenario.effect.impl.state.RenderState;
  35 
  36 /**
  37  * The implementation base class for {@link Effect} subclasses that operate
  38  * by filtering the inputs at the pixel level.
  39  */
  40 public abstract class FilterEffect<T extends RenderState> extends Effect {
  41     protected FilterEffect() {
  42         super();
  43     }
  44 
  45     protected FilterEffect(Effect input) {
  46         super(input);
  47     }
  48 
  49     protected FilterEffect(Effect input1, Effect input2) {
  50         super(input1, input2);
  51     }
  52 
  53     @Override



  54     public BaseBounds getBounds(BaseTransform transform,
  55                                 Effect defaultInput)
  56     {
  57         int numinputs = getNumInputs();
  58         RenderState rstate = getRenderState(null, transform, null,
  59                                             null, defaultInput);
  60         BaseTransform inputtx = rstate.getInputTransform(transform);

  61         BaseBounds ret;
  62         if (numinputs == 1) {
  63             Effect input = getDefaultedInput(0, defaultInput);
  64             ret = input.getBounds(inputtx, defaultInput);
  65         } else {
  66             BaseBounds inputBounds[] = new BaseBounds[numinputs];
  67             for (int i = 0; i < numinputs; i++) {
  68                 Effect input = getDefaultedInput(i, defaultInput);
  69                 inputBounds[i] = input.getBounds(inputtx, defaultInput);
  70             }
  71             ret = combineBounds(inputBounds);
  72         }
  73         return transformBounds(rstate.getResultTransform(transform), ret);



  74     }
  75 




  76     protected static Rectangle untransformClip(BaseTransform transform,
  77                                                Rectangle clip)
  78     {
  79         if (transform.isIdentity() || clip == null || clip.isEmpty()) {
  80             return clip;
  81         }
  82         // We are asked to produce samples for the pixels in the
  83         // Rectangular clip.  The samples requested are delivered for
  84         // the centers of the pixels for every pixel in that range.
  85         // Thus, we need valid data for the clip inset by 0.5 pixels
  86         // all around.
  87         // But, when we untransform, we need to make sure that the data
  88         // we provide can be used to provide a valid sample for each of
  89         // those points.  If the mapped sample coordinate falls on a
  90         // non-integer coordinate then we need the data for the 4 pixels
  91         // around that point.  Thus, we need a sample for the pixel that it
  92         // falls on, and potentially a sample for the next pixel over if
  93         // we are within 0.5 pixels of the edge of those border pixels.
  94         // The full operation is then:
  95         //     clip.inset(0.5)       // reduce to requested pixel centers


 117             }
 118             return transformedBounds;
 119         }
 120         RectBounds b = new RectBounds(clip);
 121         try {
 122             b.grow(-0.5f, -0.5f);
 123             b = (RectBounds) transform.inverseTransform(b, b);
 124             b.grow(0.5f, 0.5f);
 125             transformedBounds.setBounds(b);
 126         } catch (NoninvertibleTransformException e) {
 127             // Non-invertible means the transform has collapsed onto
 128             // a point or line and so the results of the effect are
 129             // not visible so we can use the empty bounds object we
 130             // created for transformedBounds.  Ideally this would be
 131             // checked further up in the chain, but in case we get here
 132             // we might as well do as little work as we can.
 133         }
 134         return transformedBounds;
 135     }
 136 
 137     /**
 138      * Returns the object representing the rendering strategy and state for
 139      * the filter operation characterized by the specified arguments.
 140      * This call can also be used to get a state object for non-rendering
 141      * cases like querying the bounds of an operation in which case the
 142      * {@link FilterContext} object may be null.
 143      * {@code outputClip} and {@code renderHelper} may always be null just
 144      * as they may be null for a given filter operation.
 145      * 
 146      * @param fctx the context object that would be used by the Renderer
 147      *             if this call is preparing for a render operation, or null
 148      * @param transform the transform for the output of this operation
 149      * @param outputClip the clip rectangle that may restrict this operation, or null
 150      * @param renderHelper the rendering helper object that can be used to shortcut
 151      *                     this operation under certain conditions, or null
 152      * @param defaultInput the {@link Effect} to be used in place of any null inputs
 153      * @return 
 154      */
 155     public abstract T getRenderState(FilterContext fctx,
 156                                      BaseTransform transform,
 157                                      Rectangle outputClip,
 158                                      Object renderHelper,
 159                                      Effect defaultInput);
 160 
 161     @Override
 162     public ImageData filter(FilterContext fctx,
 163                             BaseTransform transform,
 164                             Rectangle outputClip,
 165                             Object renderHelper,
 166                             Effect defaultInput)
 167     {
 168         T rstate = getRenderState(fctx, transform, outputClip,
 169                                   renderHelper, defaultInput);
 170         int numinputs = getNumInputs();
 171         ImageData inputDatas[] = new ImageData[numinputs];
 172         Rectangle filterClip;
 173         BaseTransform inputtx = rstate.getInputTransform(transform);
 174         BaseTransform resulttx = rstate.getResultTransform(transform);
 175         if (resulttx.isIdentity()) {
 176             filterClip = outputClip;
 177         } else {
 178             filterClip = untransformClip(resulttx, outputClip);

 179         }
 180         for (int i = 0; i < numinputs; i++) {
 181             Effect input = getDefaultedInput(i, defaultInput);
 182             inputDatas[i] =
 183                 input.filter(fctx, inputtx,
 184                              rstate.getInputClip(i, filterClip),
 185                              null, defaultInput);
 186             if (!inputDatas[i].validate(fctx)) {
 187                 for (int j = 0; j <= i; j++) {
 188                     inputDatas[j].unref();
 189                 }
 190                 return new ImageData(fctx, null, null);
 191             }
 192         }
 193         ImageData ret = filterImageDatas(fctx, inputtx, filterClip, rstate, inputDatas);
 194         for (int i = 0; i < numinputs; i++) {
 195             inputDatas[i].unref();
 196         }
 197         if (!resulttx.isIdentity()) {
 198             if (renderHelper instanceof ImageDataRenderer) {
 199                 ImageDataRenderer renderer = (ImageDataRenderer) renderHelper;
 200                 renderer.renderImage(ret, resulttx, fctx);
 201                 ret.unref();
 202                 ret = null;
 203             } else {
 204                 ret = ret.transform(resulttx);
 205             }
 206         }
 207         return ret;
 208     }
 209 
 210     @Override
 211     public Point2D transform(Point2D p, Effect defaultInput) {
 212         return getDefaultedInput(0, defaultInput).transform(p, defaultInput);
 213     }
 214 
 215     @Override
 216     public Point2D untransform(Point2D p, Effect defaultInput) {
 217         return getDefaultedInput(0, defaultInput).untransform(p, defaultInput);
 218     }
 219 
 220     protected abstract ImageData filterImageDatas(FilterContext fctx,
 221                                                   BaseTransform transform,
 222                                                   Rectangle outputClip,
 223                                                   T rstate,
 224                                                   ImageData... inputDatas);
 225 }