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.Rectangle;
022: import java.awt.RenderingHints;
023: import java.awt.Shape;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Rectangle2D;
026: import java.awt.image.RenderedImage;
027: import java.awt.image.renderable.RenderContext;
028:
029: import org.apache.batik.ext.awt.image.GraphicsUtil;
030: import org.apache.batik.ext.awt.image.Light;
031: import org.apache.batik.ext.awt.image.PadMode;
032: import org.apache.batik.ext.awt.image.rendered.AffineRed;
033: import org.apache.batik.ext.awt.image.rendered.BumpMap;
034: import org.apache.batik.ext.awt.image.rendered.CachableRed;
035: import org.apache.batik.ext.awt.image.rendered.PadRed;
036: import org.apache.batik.ext.awt.image.rendered.SpecularLightingRed;
037:
038: /**
039: * Implementation of the SpecularLightRable interface.
040: *
041: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
042: * @version $Id: SpecularLightingRable8Bit.java 475477 2006-11-15 22:44:28Z cam $
043: */
044: public class SpecularLightingRable8Bit extends
045: AbstractColorInterpolationRable implements
046: SpecularLightingRable {
047: /**
048: * Surface Scale
049: */
050: private double surfaceScale;
051:
052: /**
053: * Specular constant
054: */
055: private double ks;
056:
057: /**
058: * Specular exponent
059: */
060: private double specularExponent;
061:
062: /**
063: * Light used for the specular lighting computations
064: */
065: private Light light;
066:
067: /**
068: * Lit Area
069: */
070: private Rectangle2D litRegion;
071:
072: /**
073: * The dx/dy to use in user space for the sobel gradient.
074: */
075: private float[] kernelUnitLength = null;
076:
077: public SpecularLightingRable8Bit(Filter src, Rectangle2D litRegion,
078: Light light, double ks, double specularExponent,
079: double surfaceScale, double[] kernelUnitLength) {
080: super (src, null);
081: setLight(light);
082: setKs(ks);
083: setSpecularExponent(specularExponent);
084: setSurfaceScale(surfaceScale);
085: setLitRegion(litRegion);
086: setKernelUnitLength(kernelUnitLength);
087: }
088:
089: /**
090: * Returns the source to be filtered
091: */
092: public Filter getSource() {
093: return (Filter) getSources().get(0);
094: }
095:
096: /**
097: * Sets the source to be filtered
098: */
099: public void setSource(Filter src) {
100: init(src, null);
101: }
102:
103: /**
104: * Returns this filter's bounds
105: */
106: public Rectangle2D getBounds2D() {
107: return (Rectangle2D) (litRegion.clone());
108: }
109:
110: /**
111: * Returns this filter's litRegion
112: */
113: public Rectangle2D getLitRegion() {
114: return getBounds2D();
115: }
116:
117: /**
118: * Set this filter's litRegion
119: */
120: public void setLitRegion(Rectangle2D litRegion) {
121: touch();
122: this .litRegion = litRegion;
123: }
124:
125: /**
126: * @return Light object used for the specular lighting
127: */
128: public Light getLight() {
129: return light;
130: }
131:
132: /**
133: * @param light New Light object
134: */
135: public void setLight(Light light) {
136: touch();
137: this .light = light;
138: }
139:
140: /**
141: * @return surfaceScale
142: */
143: public double getSurfaceScale() {
144: return surfaceScale;
145: }
146:
147: /**
148: * Sets the surface scale
149: */
150: public void setSurfaceScale(double surfaceScale) {
151: touch();
152: this .surfaceScale = surfaceScale;
153: }
154:
155: /**
156: * @return specular constant, or ks.
157: */
158: public double getKs() {
159: return ks;
160: }
161:
162: /**
163: * Sets the specular constant, or ks
164: */
165: public void setKs(double ks) {
166: touch();
167: this .ks = ks;
168: }
169:
170: /**
171: * @return specular exponent
172: */
173: public double getSpecularExponent() {
174: return specularExponent;
175: }
176:
177: /**
178: * Sets the specular exponent
179: */
180: public void setSpecularExponent(double specularExponent) {
181: touch();
182: this .specularExponent = specularExponent;
183: }
184:
185: /**
186: * Returns the min [dx,dy] distance in user space for evalutation of
187: * the sobel gradient.
188: */
189: public double[] getKernelUnitLength() {
190: if (kernelUnitLength == null)
191: return null;
192:
193: double[] ret = new double[2];
194: ret[0] = kernelUnitLength[0];
195: ret[1] = kernelUnitLength[1];
196: return ret;
197: }
198:
199: /**
200: * Sets the min [dx,dy] distance in user space for evaluation of the
201: * sobel gradient. If set to zero or null then device space will be used.
202: */
203: public void setKernelUnitLength(double[] kernelUnitLength) {
204: touch();
205: if (kernelUnitLength == null) {
206: this .kernelUnitLength = null;
207: return;
208: }
209:
210: if (this .kernelUnitLength == null)
211: this .kernelUnitLength = new float[2];
212:
213: this .kernelUnitLength[0] = (float) kernelUnitLength[0];
214: this .kernelUnitLength[1] = (float) kernelUnitLength[1];
215: }
216:
217: public RenderedImage createRendering(RenderContext rc) {
218: Shape aoi = rc.getAreaOfInterest();
219: if (aoi == null)
220: aoi = getBounds2D();
221:
222: Rectangle2D aoiR = aoi.getBounds2D();
223: Rectangle2D.intersect(aoiR, getBounds2D(), aoiR);
224:
225: AffineTransform at = rc.getTransform();
226: Rectangle devRect = at.createTransformedShape(aoiR).getBounds();
227:
228: if (devRect.width == 0 || devRect.height == 0) {
229: return null;
230: }
231:
232: //
233: // SpecularLightingRed only operates on a scaled space.
234: // The following extracts the scale portion of the
235: // user to device transform
236: //
237: // The source is rendered with the scale-only transform
238: // and the rendered result is used as a bumpMap for the
239: // SpecularLightingRed filter.
240: //
241: double sx = at.getScaleX();
242: double sy = at.getScaleY();
243:
244: double shx = at.getShearX();
245: double shy = at.getShearY();
246:
247: double tx = at.getTranslateX();
248: double ty = at.getTranslateY();
249:
250: // The Scale is the "hypotonose" of the matrix vectors.
251: double scaleX = Math.sqrt(sx * sx + shy * shy);
252: double scaleY = Math.sqrt(sy * sy + shx * shx);
253:
254: if (scaleX == 0 || scaleY == 0) {
255: // Non invertible transform
256: return null;
257: }
258:
259: // These values represent the scale factor to the intermediate
260: // coordinate system where we will apply our convolution.
261: if (kernelUnitLength != null) {
262: if (scaleX >= 1 / kernelUnitLength[0])
263: scaleX = 1 / kernelUnitLength[0];
264:
265: if (scaleY >= 1 / kernelUnitLength[1])
266: scaleY = 1 / kernelUnitLength[1];
267: }
268:
269: AffineTransform scale = AffineTransform.getScaleInstance(
270: scaleX, scaleY);
271:
272: devRect = scale.createTransformedShape(aoiR).getBounds();
273:
274: // Grow for surround needs.
275: aoiR.setRect(aoiR.getX() - (2 / scaleX), aoiR.getY()
276: - (2 / scaleY), aoiR.getWidth() + (4 / scaleX), aoiR
277: .getHeight()
278: + (4 / scaleY));
279:
280: // Build texture from the source
281: rc = (RenderContext) rc.clone();
282: rc.setAreaOfInterest(aoiR);
283: rc.setTransform(scale);
284:
285: // System.out.println("scaleX / scaleY : " + scaleX + "/" + scaleY);
286:
287: CachableRed cr;
288: cr = GraphicsUtil.wrap(getSource().createRendering(rc));
289:
290: BumpMap bumpMap = new BumpMap(cr, surfaceScale, scaleX, scaleY);
291:
292: cr = new SpecularLightingRed(ks, specularExponent, light,
293: bumpMap, devRect, 1 / scaleX, 1 / scaleY,
294: isColorSpaceLinear());
295:
296: // Return sheared/rotated tiled image
297: AffineTransform shearAt = new AffineTransform(sx / scaleX, shy
298: / scaleX, shx / scaleY, sy / scaleY, tx, ty);
299:
300: if (!shearAt.isIdentity()) {
301: RenderingHints rh = rc.getRenderingHints();
302: Rectangle padRect = new Rectangle(devRect.x - 1,
303: devRect.y - 1, devRect.width + 2,
304: devRect.height + 2);
305: cr = new PadRed(cr, padRect, PadMode.REPLICATE, rh);
306:
307: cr = new AffineRed(cr, shearAt, rh);
308: }
309:
310: return cr;
311: }
312: }
|