001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.ext.awt.image.renderable;
020:
021: import java.awt.Composite;
022: import java.awt.Graphics2D;
023: import java.awt.geom.Rectangle2D;
024: import java.awt.image.RenderedImage;
025: import java.awt.image.renderable.RenderContext;
026:
027: import org.apache.batik.ext.awt.image.GraphicsUtil;
028: import org.apache.batik.ext.awt.image.PadMode;
029: import org.apache.batik.ext.awt.image.SVGComposite;
030:
031: /**
032: * Implements a filter chain. A filter chain is defined by its
033: * filter region (i.e., the bounding box of its input/output), its
034: * filter resolution and its source. Its source cannot be null,
035: * but its resolution can. <br />
036: * The filter chain decomposes as follows:
037: * <ul>
038: * <li>A pad operation that makes the input image a big as the
039: * filter region.</li>
040: * <li>If there is a filterResolution specified along at least
041: * one of the axis, a <tt>AffineRable</tt>
042: * </ul>
043: *
044: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
045: * @version $Id: FilterChainRable8Bit.java 475477 2006-11-15 22:44:28Z cam $
046: */
047: public class FilterChainRable8Bit extends AbstractRable implements
048: FilterChainRable, PaintRable {
049: /**
050: * Resolution along the X axis
051: */
052: private int filterResolutionX;
053:
054: /**
055: * Resolution along the Y axis
056: */
057: private int filterResolutionY;
058:
059: /**
060: * The chain's source
061: */
062: private Filter chainSource;
063:
064: /**
065: * Scale operation. May be null
066: */
067: private FilterResRable filterRes;
068:
069: /**
070: * Crop operation.
071: */
072: private PadRable crop;
073:
074: /**
075: * Filter region
076: */
077: private Rectangle2D filterRegion;
078:
079: /**
080: * Default constructor.
081: */
082: public FilterChainRable8Bit(Filter source, Rectangle2D filterRegion) {
083: if (source == null) {
084: throw new IllegalArgumentException();
085: }
086: if (filterRegion == null) {
087: throw new IllegalArgumentException();
088: }
089:
090: // Build crop with chain source and dummy region (will be lazily evaluated
091: // later on).
092: Rectangle2D padRect = (Rectangle2D) filterRegion.clone();
093: crop = new PadRable8Bit(source, padRect, PadMode.ZERO_PAD);
094:
095: // Keep a reference to the chain source and filter
096: // regions.
097: this .chainSource = source;
098: this .filterRegion = filterRegion;
099:
100: // crop is the real shource for this filter
101: // The filter chain is a simple passthrough to its
102: // crop node.
103: init(crop);
104:
105: }
106:
107: /**
108: * Returns the resolution along the X axis.
109: */
110: public int getFilterResolutionX() {
111: return filterResolutionX;
112: }
113:
114: /**
115: * Sets the resolution along the X axis, i.e., the maximum
116: * size for intermediate images along that axis.
117: * If filterResolutionX is less than zero, no filter resolution
118: * is forced on the filter chain. If filterResolutionX is zero,
119: * then the filter returns null. If filterResolutionX is positive,
120: * then the filter resolution is applied.
121: */
122: public void setFilterResolutionX(int filterResolutionX) {
123: touch();
124: this .filterResolutionX = filterResolutionX;
125:
126: setupFilterRes();
127: }
128:
129: /**
130: * Returns the resolution along the Y axis.
131: */
132: public int getFilterResolutionY() {
133: return filterResolutionY;
134: }
135:
136: /**
137: * Sets the resolution along the Y axis, i.e., the maximum
138: * size for intermediate images along that axis.
139: * If filterResolutionY is zero or less, the value of
140: * filterResolutionX is used.
141: */
142: public void setFilterResolutionY(int filterResolutionY) {
143: touch();
144: this .filterResolutionY = filterResolutionY;
145: setupFilterRes();
146: }
147:
148: /**
149: * Implementation. Checks the current value of the
150: * filterResolutionX and filterResolutionY attribute and
151: * setup the filterRes operation accordingly.
152: */
153: private void setupFilterRes() {
154: if (filterResolutionX >= 0) {
155: if (filterRes == null) {
156: filterRes = new FilterResRable8Bit();
157: filterRes.setSource(chainSource);
158: }
159:
160: filterRes.setFilterResolutionX(filterResolutionX);
161: filterRes.setFilterResolutionY(filterResolutionY);
162: } else {
163: // X is negative, this disables the resolution filter.
164: filterRes = null;
165: }
166:
167: // Now, update the crop source to reflect the filterRes
168: // settings.
169: if (filterRes != null) {
170: crop.setSource(filterRes);
171: } else {
172: crop.setSource(chainSource);
173: }
174: }
175:
176: /**
177: * Sets the filter output area, in user space.
178: * A null value is illegal.
179: */
180: public void setFilterRegion(Rectangle2D filterRegion) {
181: if (filterRegion == null) {
182: throw new IllegalArgumentException();
183: }
184: touch();
185: this .filterRegion = filterRegion;
186: }
187:
188: /**
189: * Returns the filter output area, in user space
190: */
191: public Rectangle2D getFilterRegion() {
192: return filterRegion;
193: }
194:
195: /**
196: * Returns the source of the chain. Note that a crop and
197: * affine operation may be inserted before the source,
198: * depending on the filterRegion and filterResolution
199: * parameters.
200: */
201: public Filter getSource() {
202: return (Filter) crop;
203: }
204:
205: /**
206: * Sets the source to be src.
207: * @param chainSource image to the chain.
208: */
209: public void setSource(Filter chainSource) {
210: if (chainSource == null) {
211: throw new IllegalArgumentException(
212: "Null Source for Filter Chain");
213: }
214: touch();
215: this .chainSource = chainSource;
216:
217: if (filterRes == null) {
218: crop.setSource(chainSource);
219: } else {
220: filterRes.setSource(chainSource);
221: }
222: }
223:
224: /**
225: * Returns this filter's bounds
226: */
227: public Rectangle2D getBounds2D() {
228: return (Rectangle2D) filterRegion.clone();
229: }
230:
231: /**
232: * Should perform the equivilent action as
233: * createRendering followed by drawing the RenderedImage to
234: * Graphics2D, or return false.
235: *
236: * @param g2d The Graphics2D to draw to.
237: * @return true if the paint call succeeded, false if
238: * for some reason the paint failed (in which
239: * case a createRendering should be used).
240: */
241: public boolean paintRable(Graphics2D g2d) {
242: // This optimization only apply if we are using
243: // SrcOver. Otherwise things break...
244: Composite c = g2d.getComposite();
245: if (!SVGComposite.OVER.equals(c))
246: return false;
247:
248: GraphicsUtil.drawImage(g2d, getSource());
249:
250: return true;
251: }
252:
253: public RenderedImage createRendering(RenderContext context) {
254: return crop.createRendering(context);
255: }
256: }
|