001: /*
002: * $RCSfile: BandMergeOpImage.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.2 $
009: * $Date: 2006/06/19 18:33:35 $
010: * $State: Exp $
011: */
012: package com.sun.media.jai.opimage;
013:
014: import java.awt.Rectangle;
015: import java.awt.image.ColorModel;
016: import java.awt.image.DataBuffer;
017: import java.awt.image.IndexColorModel;
018: import java.awt.image.Raster;
019: import java.awt.image.RenderedImage;
020: import java.awt.image.SampleModel;
021: import java.awt.image.WritableRaster;
022: import java.util.Vector;
023: import javax.media.jai.ImageLayout;
024: import javax.media.jai.PointOpImage;
025: import java.util.Map;
026: import javax.media.jai.PixelAccessor;
027: import javax.media.jai.UnpackedImageData;
028: import javax.media.jai.RasterFactory;
029: import com.sun.media.jai.util.JDKWorkarounds;
030:
031: /**
032: * An <code>OpImage</code> implementing the "BandMerge" operation as
033: * described in <code>javax.media.jai.operator.BandMergeDescriptor</code>.
034: *
035: * <p>This <code>OpImage</code> bandmerges the pixel values of two or
036: * more source images.
037: *
038: * The data type <code>byte</code> is treated as unsigned, with maximum
039: * value as 255 and minimum value as 0.
040: *
041: * There is no attempt to rescale binary images to the approapriate
042: * gray levels, such as 255 or 0. A lookup should be performed first
043: * if so desired.
044: *
045: * @since JAI 1.1
046: * @see javax.media.jai.operator.BandMergeDescriptor
047: * @see BandMergeCRIF
048: *
049: */
050: class BandMergeOpImage extends PointOpImage {
051:
052: // list of ColorModels required for IndexColorModel support
053: ColorModel[] colorModels;
054:
055: /**
056: * Constructs a <code>BandMergeOpImage</code>.
057: *
058: * <p>The <code>layout</code> parameter may optionally contain the
059: * tile grid layout, sample model, and/or color model. The image
060: * dimension is determined by the intersection of the bounding boxes
061: * of the source images.
062: *
063: * <p>The image layout of the first source image, <code>source1</code>,
064: * is used as the fallback for the image layout of the destination
065: * image. The destination number of bands is the sum of all source
066: * image bands.
067: *
068: * @param sources <code>Vector</code> of sources.
069: * @param config Configurable attributes of the image including
070: * configuration variables indexed by
071: * <code>RenderingHints.Key</code>s and image properties indexed
072: * by <code>String</code>s or <code>CaselessStringKey</code>s.
073: * This is simply forwarded to the superclass constructor.
074: * @param layout The destination image layout.
075: */
076: public BandMergeOpImage(Vector sources, Map config,
077: ImageLayout layout) {
078:
079: super (sources, layoutHelper(sources, layout), config, true);
080:
081: // Set flag to permit in-place operation.
082: permitInPlaceOperation();
083:
084: // get ColorModels for IndexColorModel support
085: int numSrcs = sources.size();
086: colorModels = new ColorModel[numSrcs];
087:
088: for (int i = 0; i < numSrcs; i++) {
089: colorModels[i] = ((RenderedImage) sources.get(i))
090: .getColorModel();
091: }
092: }
093:
094: private static int totalNumBands(Vector sources) {
095: int total = 0;
096:
097: for (int i = 0; i < sources.size(); i++) {
098: RenderedImage image = (RenderedImage) sources.get(i);
099:
100: if (image.getColorModel() instanceof IndexColorModel) {
101: total += image.getColorModel().getNumComponents();
102: } else {
103: total += image.getSampleModel().getNumBands();
104: }
105: }
106:
107: return total;
108: }
109:
110: private static ImageLayout layoutHelper(Vector sources,
111: ImageLayout il) {
112:
113: ImageLayout layout = (il == null) ? new ImageLayout()
114: : (ImageLayout) il.clone();
115:
116: int numSources = sources.size();
117:
118: // dest data type is the maximum of transfertype of source image
119: // utilizing the monotonicity of data types.
120:
121: // dest number of bands = sum of source bands
122: int destNumBands = totalNumBands(sources);
123:
124: int destDataType = DataBuffer.TYPE_BYTE; // initialize
125: RenderedImage srci = (RenderedImage) sources.get(0);
126: Rectangle destBounds = new Rectangle(srci.getMinX(), srci
127: .getMinY(), srci.getWidth(), srci.getHeight());
128: for (int i = 0; i < numSources; i++) {
129: srci = (RenderedImage) sources.get(i);
130: destBounds = destBounds.intersection(new Rectangle(srci
131: .getMinX(), srci.getMinY(), srci.getWidth(), srci
132: .getHeight()));
133:
134: int typei = srci.getSampleModel().getTransferType();
135:
136: // NOTE: this depends on JDK ordering
137: destDataType = typei > destDataType ? typei : destDataType;
138: }
139:
140: SampleModel sm = layout.getSampleModel((RenderedImage) sources
141: .get(0));
142:
143: if (sm.getNumBands() < destNumBands) {
144: int[] destOffsets = new int[destNumBands];
145:
146: for (int i = 0; i < destNumBands; i++) {
147: destOffsets[i] = i;
148: }
149:
150: // determine the proper width and height to use
151: int destTileWidth = sm.getWidth();
152: int destTileHeight = sm.getHeight();
153: if (layout.isValid(ImageLayout.TILE_WIDTH_MASK)) {
154: destTileWidth = layout
155: .getTileWidth((RenderedImage) sources.get(0));
156: }
157: if (layout.isValid(ImageLayout.TILE_HEIGHT_MASK)) {
158: destTileHeight = layout
159: .getTileHeight((RenderedImage) sources.get(0));
160: }
161:
162: sm = RasterFactory.createComponentSampleModel(sm,
163: destDataType, destTileWidth, destTileHeight,
164: destNumBands);
165:
166: layout.setSampleModel(sm);
167: }
168:
169: ColorModel cm = layout.getColorModel(null);
170:
171: if (cm != null
172: && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) {
173: // Clear the mask bit if incompatible.
174: layout.unsetValid(ImageLayout.COLOR_MODEL_MASK);
175: }
176:
177: return layout;
178: }
179:
180: /**
181: * BandMerges the pixel values of two source images within a specified
182: * rectangle.
183: *
184: * @param sources Cobbled sources, guaranteed to provide all the
185: * source data necessary for computing the rectangle.
186: * @param dest The tile containing the rectangle to be computed.
187: * @param destRect The rectangle within the tile to be computed.
188: */
189: protected void computeRect(Raster[] sources, WritableRaster dest,
190: Rectangle destRect) {
191:
192: int destType = dest.getTransferType();
193:
194: switch (destType) {
195: case DataBuffer.TYPE_BYTE:
196: byteLoop(sources, dest, destRect);
197: break;
198: case DataBuffer.TYPE_SHORT:
199: case DataBuffer.TYPE_USHORT:
200: shortLoop(sources, dest, destRect);
201: break;
202: case DataBuffer.TYPE_INT:
203: intLoop(sources, dest, destRect);
204: break;
205: case DataBuffer.TYPE_FLOAT:
206: floatLoop(sources, dest, destRect);
207: break;
208: case DataBuffer.TYPE_DOUBLE:
209: doubleLoop(sources, dest, destRect);
210: break;
211: default:
212: throw new RuntimeException();
213: }
214: }
215:
216: private void byteLoop(Raster[] sources, WritableRaster dest,
217: Rectangle destRect) {
218: int nSrcs = sources.length;
219: int[] snbands = new int[nSrcs];
220: PixelAccessor[] pas = new PixelAccessor[nSrcs];
221:
222: for (int i = 0; i < nSrcs; i++) {
223: pas[i] = new PixelAccessor(sources[i].getSampleModel(),
224: colorModels[i]);
225:
226: if (colorModels[i] instanceof IndexColorModel) {
227: snbands[i] = colorModels[i].getNumComponents();
228: } else {
229: snbands[i] = sources[i].getNumBands();
230: }
231: }
232:
233: int dnbands = dest.getNumBands();
234: int destType = dest.getTransferType();
235: PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);
236:
237: UnpackedImageData dimd = d.getPixels(dest, destRect, //liney,
238: destType, true);
239:
240: byte[][] dstdata = (byte[][]) dimd.data;
241:
242: for (int sindex = 0, db = 0; sindex < nSrcs; sindex++) {
243:
244: UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex]
245: .getComponents(sources[sindex], destRect,
246: sources[sindex].getSampleModel()
247: .getTransferType())
248: : pas[sindex].getPixels(sources[sindex], destRect,
249: sources[sindex].getSampleModel()
250: .getTransferType(), false);
251:
252: int srcPixelStride = simd.pixelStride;
253: int srcLineStride = simd.lineStride;
254: int dstPixelStride = dimd.pixelStride;
255: int dstLineStride = dimd.lineStride;
256: int dRectWidth = destRect.width;
257:
258: for (int sb = 0; sb < snbands[sindex]; sb++, db++) {
259: if (db >= dnbands) {
260: // exceeding destNumBands; should not have happened
261: break;
262: }
263:
264: byte[] dstdatabandb = dstdata[db];
265: byte[][] srcdata = (byte[][]) simd.data;
266: byte[] srcdatabandsb = srcdata[sb];
267: int srcstart = simd.bandOffsets[sb];
268: int dststart = dimd.bandOffsets[db];
269:
270: for (int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride) {
271:
272: for (int i = 0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride) {
273:
274: dstdatabandb[dstpos] = srcdatabandsb[srcpos];
275: }
276: }
277: }
278: }
279:
280: d.setPixels(dimd);
281: }
282:
283: private void shortLoop(Raster[] sources, WritableRaster dest,
284: Rectangle destRect) {
285: int nSrcs = sources.length;
286: int[] snbands = new int[nSrcs];
287: PixelAccessor[] pas = new PixelAccessor[nSrcs];
288:
289: for (int i = 0; i < nSrcs; i++) {
290: pas[i] = new PixelAccessor(sources[i].getSampleModel(),
291: colorModels[i]);
292:
293: if (colorModels[i] instanceof IndexColorModel) {
294: snbands[i] = colorModels[i].getNumComponents();
295: } else {
296: snbands[i] = sources[i].getNumBands();
297: }
298: }
299:
300: int dnbands = dest.getNumBands();
301: int destType = dest.getTransferType();
302: PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);
303:
304: UnpackedImageData dimd = d.getPixels(dest, destRect, //liney,
305: destType, true);
306:
307: short[][] dstdata = (short[][]) dimd.data;
308:
309: for (int sindex = 0, db = 0; sindex < nSrcs; sindex++) {
310:
311: UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex]
312: .getComponents(sources[sindex], destRect,
313: sources[sindex].getSampleModel()
314: .getTransferType())
315: : pas[sindex].getPixels(sources[sindex], destRect,
316: sources[sindex].getSampleModel()
317: .getTransferType(), false);
318:
319: int srcPixelStride = simd.pixelStride;
320: int srcLineStride = simd.lineStride;
321: int dstPixelStride = dimd.pixelStride;
322: int dstLineStride = dimd.lineStride;
323: int dRectWidth = destRect.width;
324:
325: for (int sb = 0; sb < snbands[sindex]; sb++, db++) {
326: if (db < dnbands) {
327: short[][] srcdata = (short[][]) simd.data;
328: int srcstart = simd.bandOffsets[sb];
329: int dststart = dimd.bandOffsets[db];
330: for (int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride) {
331:
332: for (int i = 0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride) {
333:
334: dstdata[db][dstpos] = srcdata[sb][srcpos];
335: }
336: }
337: }
338: }
339: }
340:
341: d.setPixels(dimd);
342: }
343:
344: private void intLoop(Raster[] sources, WritableRaster dest,
345: Rectangle destRect) {
346: int nSrcs = sources.length;
347: int[] snbands = new int[nSrcs];
348: PixelAccessor[] pas = new PixelAccessor[nSrcs];
349:
350: for (int i = 0; i < nSrcs; i++) {
351: pas[i] = new PixelAccessor(sources[i].getSampleModel(),
352: colorModels[i]);
353:
354: if (colorModels[i] instanceof IndexColorModel) {
355: snbands[i] = colorModels[i].getNumComponents();
356: } else {
357: snbands[i] = sources[i].getNumBands();
358: }
359: }
360:
361: int dnbands = dest.getNumBands();
362: int destType = dest.getTransferType();
363: PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);
364:
365: UnpackedImageData dimd = d.getPixels(dest, destRect, //liney,
366: destType, true);
367:
368: int[][] dstdata = (int[][]) dimd.data;
369:
370: for (int sindex = 0, db = 0; sindex < nSrcs; sindex++) {
371:
372: UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex]
373: .getComponents(sources[sindex], destRect,
374: sources[sindex].getSampleModel()
375: .getTransferType())
376: : pas[sindex].getPixels(sources[sindex], destRect,
377: sources[sindex].getSampleModel()
378: .getTransferType(), false);
379:
380: int srcPixelStride = simd.pixelStride;
381: int srcLineStride = simd.lineStride;
382: int dstPixelStride = dimd.pixelStride;
383: int dstLineStride = dimd.lineStride;
384: int dRectWidth = destRect.width;
385:
386: for (int sb = 0; sb < snbands[sindex]; sb++, db++) {
387: if (db < dnbands) {
388: int[][] srcdata = (int[][]) simd.data;
389: int srcstart = simd.bandOffsets[sb];
390: int dststart = dimd.bandOffsets[db];
391: for (int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride) {
392:
393: for (int i = 0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride) {
394:
395: dstdata[db][dstpos] = srcdata[sb][srcpos];
396: }
397: }
398: }
399: }
400: }
401:
402: d.setPixels(dimd);
403: }
404:
405: private void floatLoop(Raster[] sources, WritableRaster dest,
406: Rectangle destRect) {
407:
408: int nSrcs = sources.length;
409: int[] snbands = new int[nSrcs];
410: PixelAccessor[] pas = new PixelAccessor[nSrcs];
411:
412: for (int i = 0; i < nSrcs; i++) {
413: pas[i] = new PixelAccessor(sources[i].getSampleModel(),
414: colorModels[i]);
415:
416: if (colorModels[i] instanceof IndexColorModel) {
417: snbands[i] = colorModels[i].getNumComponents();
418: } else {
419: snbands[i] = sources[i].getNumBands();
420: }
421: }
422:
423: int dnbands = dest.getNumBands();
424: int destType = dest.getTransferType();
425: PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);
426:
427: UnpackedImageData dimd = d.getPixels(dest, destRect, //liney,
428: destType, true);
429:
430: float[][] dstdata = (float[][]) dimd.data;
431:
432: for (int sindex = 0, db = 0; sindex < nSrcs; sindex++) {
433:
434: UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex]
435: .getComponents(sources[sindex], destRect,
436: sources[sindex].getSampleModel()
437: .getTransferType())
438: : pas[sindex].getPixels(sources[sindex], destRect,
439: sources[sindex].getSampleModel()
440: .getTransferType(), false);
441:
442: int srcPixelStride = simd.pixelStride;
443: int srcLineStride = simd.lineStride;
444: int dstPixelStride = dimd.pixelStride;
445: int dstLineStride = dimd.lineStride;
446: int dRectWidth = destRect.width;
447:
448: for (int sb = 0; sb < snbands[sindex]; sb++, db++) {
449: if (db < dnbands) {
450: float[][] srcdata = (float[][]) simd.data;
451: int srcstart = simd.bandOffsets[sb];
452: int dststart = dimd.bandOffsets[db];
453: for (int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride) {
454:
455: for (int i = 0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride) {
456:
457: dstdata[db][dstpos] = srcdata[sb][srcpos];
458: }
459: }
460: }
461: }
462: }
463:
464: d.setPixels(dimd);
465: }
466:
467: private void doubleLoop(Raster[] sources, WritableRaster dest,
468: Rectangle destRect) {
469:
470: int nSrcs = sources.length;
471: int[] snbands = new int[nSrcs];
472: PixelAccessor[] pas = new PixelAccessor[nSrcs];
473:
474: for (int i = 0; i < nSrcs; i++) {
475: pas[i] = new PixelAccessor(sources[i].getSampleModel(),
476: colorModels[i]);
477:
478: if (colorModels[i] instanceof IndexColorModel) {
479: snbands[i] = colorModels[i].getNumComponents();
480: } else {
481: snbands[i] = sources[i].getNumBands();
482: }
483: }
484:
485: int dnbands = dest.getNumBands();
486: int destType = dest.getTransferType();
487: PixelAccessor d = new PixelAccessor(dest.getSampleModel(), null);
488:
489: UnpackedImageData dimd = d.getPixels(dest, destRect, //liney,
490: destType, true);
491:
492: double[][] dstdata = (double[][]) dimd.data;
493:
494: for (int sindex = 0, db = 0; sindex < nSrcs; sindex++) {
495:
496: UnpackedImageData simd = colorModels[sindex] instanceof IndexColorModel ? pas[sindex]
497: .getComponents(sources[sindex], destRect,
498: sources[sindex].getSampleModel()
499: .getTransferType())
500: : pas[sindex].getPixels(sources[sindex], destRect,
501: sources[sindex].getSampleModel()
502: .getTransferType(), false);
503:
504: int srcPixelStride = simd.pixelStride;
505: int srcLineStride = simd.lineStride;
506: int dstPixelStride = dimd.pixelStride;
507: int dstLineStride = dimd.lineStride;
508: int dRectWidth = destRect.width;
509:
510: for (int sb = 0; sb < snbands[sindex]; sb++, db++) {
511: if (db < dnbands) {
512: double[][] srcdata = (double[][]) simd.data;
513: int srcstart = simd.bandOffsets[sb];
514: int dststart = dimd.bandOffsets[db];
515: for (int y = 0; y < destRect.height; y++, srcstart += srcLineStride, dststart += dstLineStride) {
516:
517: for (int i = 0, srcpos = srcstart, dstpos = dststart; i < dRectWidth; i++, srcpos += srcPixelStride, dstpos += dstPixelStride) {
518:
519: dstdata[db][dstpos] = srcdata[sb][srcpos];
520: }
521: }
522: }
523: }
524: }
525:
526: d.setPixels(dimd);
527: }
528: }
|