1 /* 2 * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 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.DirtyRegionContainer; 31 import com.sun.javafx.geom.DirtyRegionPool; 32 import com.sun.javafx.geom.Rectangle; 33 import com.sun.javafx.geom.transform.BaseTransform; 34 35 /** 36 * An effect that returns a cropped version of the input. 37 */ 38 public class Crop extends CoreEffect { 39 40 // TODO: This class should go away once we fix RT-1347... 41 42 /** 43 * Constructs a new {@code Crop} effect which crops the output of 44 * the specified source {@code Effect} to the bounds of the default 45 * input. 46 * 47 * @param source the input {@code Effect} for image data 48 */ 49 public Crop(Effect source) { 50 this(source, DefaultInput); 51 } 52 53 /** 54 * Constructs a new {@code Crop} effect to crop the output of the 55 * specified source {@code Effect} to the bounds of the specified 56 * bounds {@code Effect}. 57 * 58 * @param source the input {@code Effect} for image data 59 * @param boundsInput the input {@code Effect} that controls bounds 60 */ 61 public Crop(Effect source, Effect boundsInput) { 62 super(source, boundsInput); 63 updatePeerKey("Crop"); 64 } 65 66 /** 67 * Returns the input for this {@code Effect}. 68 * 69 * @return the input for this {@code Effect} 70 */ 71 public final Effect getInput() { 72 return getInputs().get(0); 73 } 74 75 /** 76 * Sets the source input for this {@code Effect} to a specific 77 * {@code Effect} or to the default input if {@code input} is 78 * {@code null}. 79 * 80 * @param input the input for this {@code Effect} 81 */ 82 public void setInput(Effect input) { 83 setInput(0, input); 84 } 85 86 /** 87 * Returns the bounds input for this {@code Effect}. 88 * 89 * @return the bounds input for this {@code Effect} 90 */ 91 public final Effect getBoundsInput() { 92 return getInputs().get(1); 93 } 94 95 /** 96 * Sets the bounds input for this {@code Effect}. 97 * Sets the bounds input for this {@code Effect} to a specific 98 * {@code Effect} or to the default input if {@code input} is 99 * {@code null}. 100 * 101 * @param input the bounds input for this {@code Effect} 102 */ 103 public void setBoundsInput(Effect input) { 104 setInput(1, input); 105 } 106 107 private Effect getBoundsInput(Effect defaultInput) { 108 return getDefaultedInput(1, defaultInput); 109 } 110 111 @Override 112 public BaseBounds getBounds(BaseTransform transform, Effect defaultInput) { 113 return getBoundsInput(defaultInput).getBounds(transform, defaultInput); 114 } 115 116 /** 117 * Transform the specified point {@code p} from the coordinate space 118 * of the primary content input to the coordinate space of the effect 119 * output. 120 * In essence, this method asks the question "Which output coordinate 121 * is most affected by the data at the specified coordinate in the 122 * primary source input?" 123 * <p> 124 * The {@code Crop} effect delegates this operation to its primary 125 * (non-bounds) input, or the {@code defaultInput} if the primary 126 * input is {@code null}. 127 * 128 * @param p the point in the coordinate space of the primary content 129 * input to be transformed 130 * @param defaultInput the default input {@code Effect} to be used in 131 * all cases where a filter has a null input 132 * @return the transformed point in the coordinate space of the result 133 */ 134 @Override 135 public Point2D transform(Point2D p, Effect defaultInput) { 136 return getDefaultedInput(0, defaultInput).transform(p, defaultInput); 137 } 138 139 /** 140 * Transform the specified point {@code p} from the coordinate space 141 * of the output of the effect into the coordinate space of the 142 * primary content input. 143 * In essence, this method asks the question "Which source coordinate 144 * contributes most to the definition of the output at the specified 145 * coordinate?" 146 * <p> 147 * The {@code Crop} effect delegates this operation to its primary 148 * (non-bounds) input, or the {@code defaultInput} if the primary 149 * input is {@code null}. 150 * 151 * @param p the point in the coordinate space of the result output 152 * to be transformed 153 * @param defaultInput the default input {@code Effect} to be used in 154 * all cases where a filter has a null input 155 * @return the untransformed point in the coordinate space of the 156 * primary content input 157 */ 158 @Override 159 public Point2D untransform(Point2D p, Effect defaultInput) { 160 return getDefaultedInput(0, defaultInput).untransform(p, defaultInput); 161 } 162 163 @Override 164 public ImageData filter(FilterContext fctx, 165 BaseTransform transform, 166 Rectangle outputClip, 167 Object renderHelper, 168 Effect defaultInput) 169 { 170 Effect input1 = getDefaultedInput(1, defaultInput); 171 BaseBounds cropBounds = input1.getBounds(transform, defaultInput); 172 Rectangle cropRect = new Rectangle(cropBounds); 173 cropRect.intersectWith(outputClip); 174 Effect input0 = getDefaultedInput(0, defaultInput); 175 ImageData id = input0.filter(fctx, transform, cropRect, null, defaultInput); 176 if (!id.validate(fctx)) { 177 return new ImageData(fctx, null, null); 178 } 179 ImageData ret = filterImageDatas(fctx, transform, cropRect, id); 180 id.unref(); 181 return ret; 182 } 183 184 @Override 185 protected Rectangle getInputClip(int inputIndex, 186 BaseTransform transform, 187 Rectangle outputClip) 188 { 189 // RT-27564 190 // TODO: Since we also crop to the "crop input" and since cropping 191 // is a form of clipping, we could further restrict the bounds we 192 // ask from the content input here... 193 return outputClip; 194 } 195 196 @Override 197 public boolean reducesOpaquePixels() { 198 return true; 199 } 200 201 @Override 202 public DirtyRegionContainer getDirtyRegions(Effect defaultInput, DirtyRegionPool regionPool) { 203 Effect di0 = getDefaultedInput(0, defaultInput); 204 DirtyRegionContainer drc = di0.getDirtyRegions(defaultInput, regionPool); 205 Effect di1 = getDefaultedInput(1, defaultInput); 206 BaseBounds cropBounds = di1.getBounds(BaseTransform.IDENTITY_TRANSFORM, defaultInput); 207 for (int i = 0; i < drc.size(); i++) { 208 drc.getDirtyRegion(i).intersectWith(cropBounds); 209 if (drc.checkAndClearRegion(i)) { 210 --i; 211 } 212 } 213 214 return drc; 215 } 216 }