001: /*
002: * $RCSfile: BorderExtenderReflect.java,v $
003: *
004: * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Use is subject to license terms.
007: *
008: * $Revision: 1.1 $
009: * $Date: 2005/02/11 04:57:04 $
010: * $State: Exp $
011: */
012: package javax.media.jai;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.DataBuffer;
016: import java.awt.image.Raster;
017: import java.awt.image.WritableRaster;
018: import com.sun.media.jai.util.JDKWorkarounds;
019:
020: /**
021: * A subclass of <code>BorderExtender</code> that implements
022: * border extension by filling all pixels outside of the image
023: * bounds with copies of the whole image. For example, the image:
024: *
025: * <p><center>
026: * <table width="10%" border=1>
027: * <tr align=center><td><tt>|><br>|\</td> </tr>
028: * </table></center>
029: *
030: * <br>if extended by adding two extra rows to the top and bottom and
031: * one extra column on the left and right sides, would become:
032: *
033: * <p><center>
034: * <table width="30%" border=1>
035: * <tr align=center><td><tt><|<br>/|</tt></td> <td><tt>|><br>|\</tt></td> <td><tt><|<br>/|</tt></td> </tr>
036: * <tr align=center><td><tt>\|<br><|</tt></td> <td><tt>|/<br>|></tt></td> <td><tt>\|<br><|</tt></td> </tr>
037: * <tr align=center><td><tt><|<br>/|</tt></td> <td><tt>|><br>|\</tt></td> <td><tt><|<br>/|</tt></td> </tr>
038: * <tr align=center><td><tt>\|<br><|</tt></td> <td><tt>|/<br>|></tt></td> <td><tt>\|<br><|</tt></td> </tr>
039: * <tr align=center><td><tt><|<br>/|</tt></td> <td><tt>|><br>|\</tt></td> <td><tt><|<br>/|</tt></td> </tr>
040: * </table></center>
041: *
042: * <p> This form of extension avoids discontinuities around the edges
043: * of the image.
044: */
045: public final class BorderExtenderReflect extends BorderExtender {
046:
047: BorderExtenderReflect() {
048: }
049:
050: private void flipX(WritableRaster raster) {
051: int minX = raster.getMinX();
052: int minY = raster.getMinY();
053: int height = raster.getHeight();
054: int width = raster.getWidth();
055: int maxX = minX + width - 1; // Inclusive
056: int numBands = raster.getNumBands();
057:
058: switch (raster.getSampleModel().getDataType()) {
059: case DataBuffer.TYPE_BYTE:
060: case DataBuffer.TYPE_SHORT:
061: case DataBuffer.TYPE_USHORT:
062: case DataBuffer.TYPE_INT:
063: int[] iData0 = new int[height * numBands];
064: int[] iData1 = new int[height * numBands];
065:
066: for (int i = 0; i < width / 2; i++) {
067: raster.getPixels(minX + i, minY, 1, height, iData0);
068: raster.getPixels(maxX - i, minY, 1, height, iData1);
069:
070: raster.setPixels(minX + i, minY, 1, height, iData1);
071: raster.setPixels(maxX - i, minY, 1, height, iData0);
072: }
073: break;
074:
075: case DataBuffer.TYPE_FLOAT:
076: float[] fData0 = new float[height * numBands];
077: float[] fData1 = new float[height * numBands];
078:
079: for (int i = 0; i < width / 2; i++) {
080: raster.getPixels(minX + i, minY, 1, height, fData0);
081: raster.getPixels(maxX - i, minY, 1, height, fData1);
082:
083: raster.setPixels(minX + i, minY, 1, height, fData1);
084: raster.setPixels(maxX - i, minY, 1, height, fData0);
085: }
086: break;
087:
088: case DataBuffer.TYPE_DOUBLE:
089: double[] dData0 = new double[height * numBands];
090: double[] dData1 = new double[height * numBands];
091:
092: for (int i = 0; i < width / 2; i++) {
093: raster.getPixels(minX + i, minY, 1, height, dData0);
094: raster.getPixels(maxX - i, minY, 1, height, dData1);
095:
096: raster.setPixels(minX + i, minY, 1, height, dData1);
097: raster.setPixels(maxX - i, minY, 1, height, dData0);
098: }
099: break;
100: }
101: }
102:
103: private void flipY(WritableRaster raster) {
104: int minX = raster.getMinX();
105: int minY = raster.getMinY();
106: int height = raster.getHeight();
107: int width = raster.getWidth();
108: int maxY = minY + height - 1; // Inclusive
109: int numBands = raster.getNumBands();
110:
111: switch (raster.getSampleModel().getDataType()) {
112: case DataBuffer.TYPE_BYTE:
113: case DataBuffer.TYPE_SHORT:
114: case DataBuffer.TYPE_USHORT:
115: case DataBuffer.TYPE_INT:
116: int[] iData0 = new int[width * numBands];
117: int[] iData1 = new int[width * numBands];
118:
119: for (int i = 0; i < height / 2; i++) {
120: raster.getPixels(minX, minY + i, width, 1, iData0);
121: raster.getPixels(minX, maxY - i, width, 1, iData1);
122:
123: raster.setPixels(minX, minY + i, width, 1, iData1);
124: raster.setPixels(minX, maxY - i, width, 1, iData0);
125: }
126: break;
127:
128: case DataBuffer.TYPE_FLOAT:
129: float[] fData0 = new float[width * numBands];
130: float[] fData1 = new float[width * numBands];
131:
132: for (int i = 0; i < height / 2; i++) {
133: raster.getPixels(minX, minY + i, width, 1, fData0);
134: raster.getPixels(minX, maxY - i, width, 1, fData1);
135:
136: raster.setPixels(minX, minY + i, width, 1, fData1);
137: raster.setPixels(minX, maxY - i, width, 1, fData0);
138: }
139: break;
140:
141: case DataBuffer.TYPE_DOUBLE:
142: double[] dData0 = new double[width * numBands];
143: double[] dData1 = new double[width * numBands];
144:
145: for (int i = 0; i < height / 2; i++) {
146: raster.getPixels(minX, minY + i, width, 1, dData0);
147: raster.getPixels(minX, maxY - i, width, 1, dData1);
148:
149: raster.setPixels(minX, minY + i, width, 1, dData1);
150: raster.setPixels(minX, maxY - i, width, 1, dData0);
151: }
152: break;
153: }
154: }
155:
156: /**
157: * Fills in the portions of a given <code>Raster</code> that lie
158: * outside the bounds of a given <code>PlanarImage</code> with
159: * suitably reflected copies of the entire image.
160: *
161: * <p> The portion of <code>raster</code> that lies within
162: * <code>im.getBounds()</code> is not altered.
163: *
164: * @param raster The <code>WritableRaster</code> the border area of
165: * which is to be filled with suitably reflected copies
166: * of portions of the specified image.
167: * @param im The <code>PlanarImage</code> the data of which is
168: * to be reflected and used to fill the
169: * <code>WritableRaster</code> border.
170: *
171: * @throws <code>IllegalArgumentException</code> if either parameter is
172: * <code>null</code>.
173: */
174: public final void extend(WritableRaster raster, PlanarImage im) {
175:
176: if (raster == null || im == null) {
177: throw new IllegalArgumentException(JaiI18N
178: .getString("Generic0"));
179: }
180:
181: int width = raster.getWidth();
182: int height = raster.getHeight();
183:
184: int minX = raster.getMinX();
185: int maxX = minX + width;
186: int minY = raster.getMinY();
187: int maxY = minY + height;
188:
189: int imMinX = im.getMinX();
190: int imMinY = im.getMinY();
191: int imWidth = im.getWidth();
192: int imHeight = im.getHeight();
193:
194: int validMinX = Math.max(imMinX, minX);
195: int validMaxX = Math.min(imMinX + imWidth, maxX);
196: int validMinY = Math.max(imMinY, minY);
197: int validMaxY = Math.min(imMinY + imHeight, maxY);
198:
199: if (validMinX > validMaxX || validMinY > validMaxY) {
200: // Raster does not intersect image. Determine the location
201: // and size of the smallest rectangle containing the Raster
202: // and which intersects the image.
203: if (validMinX > validMaxX) { // no intersetion in X
204: if (minX == validMinX) {
205: minX = im.getMaxX() - 1;
206: } else {
207: maxX = im.getMinX();
208: }
209: }
210: if (validMinY > validMaxY) { // no intersetion in Y
211: if (minY == validMinY) {
212: minY = im.getMaxY() - 1;
213: } else {
214: maxY = im.getMinY();
215: }
216: }
217:
218: // Create minimum Raster.
219: WritableRaster wr = raster.createCompatibleWritableRaster(
220: minX, minY, maxX - minX, maxY - minY);
221:
222: // Extend the data.
223: extend(wr, im);
224:
225: // Create a child with same bounds as the target Raster.
226: Raster child = wr.createChild(raster.getMinX(), raster
227: .getMinY(), raster.getWidth(), raster.getHeight(),
228: raster.getMinX(), raster.getMinY(), null);
229:
230: // Copy the data from the child.
231: JDKWorkarounds.setRect(raster, child, 0, 0);
232:
233: return;
234: }
235:
236: Rectangle rect = new Rectangle();
237:
238: // Notionally extend the source image by treating it as a single
239: // tile of an infinite tiled image. Adjacent tiles are reflections
240: // of one another.
241:
242: // Compute the min and max X and Y tile indices of the area
243: // intersected by the output raster.
244: int minTileX = PlanarImage.XToTileX(minX, imMinX, imWidth);
245: int maxTileX = PlanarImage.XToTileX(maxX - 1, imMinX, imWidth);
246: int minTileY = PlanarImage.YToTileY(minY, imMinY, imHeight);
247: int maxTileY = PlanarImage.YToTileY(maxY - 1, imMinY, imHeight);
248:
249: // Loop over the tiles
250: for (int tileY = minTileY; tileY <= maxTileY; tileY++) {
251: int ty = tileY * imHeight + imMinY;
252: for (int tileX = minTileX; tileX <= maxTileX; tileX++) {
253: int tx = tileX * imWidth + imMinX;
254:
255: // Don't touch the central "tile" (actual image)
256: if (tileX == 0 && tileY == 0) {
257: continue;
258: }
259:
260: boolean flipX = (Math.abs(tileX) % 2) == 1;
261: boolean flipY = (Math.abs(tileY) % 2) == 1;
262:
263: // Clip the tile bounds against the bounds of the Raster.
264: // Keep track of the (x, y) offset of the start of the tile.
265: rect.x = tx;
266: rect.y = ty;
267: rect.width = imWidth;
268: rect.height = imHeight;
269:
270: int xOffset = 0;
271: if (rect.x < minX) {
272: xOffset = minX - rect.x;
273: rect.x = minX;
274: rect.width -= xOffset;
275: }
276: int yOffset = 0;
277: if (rect.y < minY) {
278: yOffset = minY - rect.y;
279: rect.y = minY;
280: rect.height -= yOffset;
281: }
282: if (rect.x + rect.width > maxX) {
283: rect.width = maxX - rect.x;
284: }
285: if (rect.y + rect.height > maxY) {
286: rect.height = maxY - rect.y;
287: }
288:
289: int imX;
290: if (flipX) {
291: if (xOffset == 0) {
292: imX = imMinX + imWidth - rect.width;
293: } else {
294: imX = imMinX;
295: }
296: } else {
297: imX = imMinX + xOffset;
298: }
299:
300: int imY;
301: if (flipY) {
302: if (yOffset == 0) {
303: imY = imMinY + imHeight - rect.height;
304: } else {
305: imY = imMinY;
306: }
307: } else {
308: imY = imMinY + yOffset;
309: }
310:
311: // Create a child raster with coordinates within the
312: // actual image.
313: WritableRaster child = RasterFactory
314: .createWritableChild(raster, rect.x, rect.y,
315: rect.width, rect.height, imX, imY, null);
316:
317: // Copy the data into the Raster
318: im.copyData(child);
319:
320: if (flipX) {
321: flipX(child);
322: }
323:
324: if (flipY) {
325: flipY(child);
326: }
327: }
328: }
329: }
330: }
|