/* JAI-Ext - OpenSource Java Advanced Image Extensions Library * http://www.geo-solutions.it/ * Copyright 2014 GeoSolutions * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.geosolutions.jaiext.bandselect; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.PixelInterleavedSampleModel; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.SampleModel; import java.awt.image.SinglePixelPackedSampleModel; import java.awt.image.WritableRaster; import java.util.Map; import javax.media.jai.ImageLayout; import javax.media.jai.PointOpImage; import com.sun.media.jai.util.JDKWorkarounds; /** * An <code>OpImage</code> implementing the "BandSelect" operation. * * <p>This <code>OpImage</code> copies the specified bands of the source * image to the destination image in the order that is specified. * * @see javax.media.jai.operator.BandSelectDescriptor * @see BandSelectCRIF * * * @author Simone Giannecchini, GeoSolutions SAS * @since 1.0 */ @SuppressWarnings("unchecked") public class BandSelectOpImage extends PointOpImage { // Set if the source has a SinglePixelPackedSampleModel and // bands.length < 3. private boolean triggerCopyOperation; private int[] bands; private static ImageLayout layoutHelper(ImageLayout layout, RenderedImage source, int[] bandIndices) { ImageLayout il = layout == null ? new ImageLayout() : (ImageLayout)layout.clone(); // Create a sub-banded SampleModel. SampleModel sourceSM = source.getSampleModel(); int numBands = bandIndices.length; // The only ColorModel compatible with a SinglePixelPackedSampleModel // in the J2SE is a DirectColorModel which is by definition of // ColorSpace.TYPE_RGB. Therefore if there are fewer than 3 bands // a data copy is obligatory if a ColorModel will be possible. SampleModel sm = null; if(sourceSM instanceof SinglePixelPackedSampleModel && numBands < 3) { sm = new PixelInterleavedSampleModel( DataBuffer.TYPE_BYTE, sourceSM.getWidth(), sourceSM.getHeight(), numBands, sourceSM.getWidth()*numBands, numBands == 1 ? new int[] {0} : new int[] {0, 1}); } else { sm = sourceSM.createSubsetSampleModel(bandIndices); } il.setSampleModel(sm); // Clear the ColorModel mask if needed. ColorModel cm = il.getColorModel(null); if(cm != null && !JDKWorkarounds.areCompatibleDataModels(sm, cm)) { // Clear the mask bit if incompatible. il.unsetValid(ImageLayout.COLOR_MODEL_MASK); } // Force the tile grid to be identical to that of the source. il.setTileGridXOffset(source.getTileGridXOffset()); il.setTileGridYOffset(source.getTileGridYOffset()); il.setTileWidth(source.getTileWidth()); il.setTileHeight(source.getTileHeight()); return il; } /** * Constructor. * * @param source The source image. * @param layout The destination image layout. * @param bands The selected band indices of the source. * The number of bands of the destination is * determined by <code>bands.length</code>. */ public BandSelectOpImage(RenderedImage source, Map<?,?> config, ImageLayout layout, int[] bandIndices) { super(vectorize(source), layoutHelper(layout, source, bandIndices), config, true); this.triggerCopyOperation = source.getSampleModel() instanceof SinglePixelPackedSampleModel && bandIndices.length < 3; this.bands = (int[])bandIndices.clone(); } public boolean computesUniqueTiles() { return triggerCopyOperation; } public Raster computeTile(int tileX, int tileY) { Raster tile = getSourceImage(0).getTile(tileX, tileY); if (triggerCopyOperation) { // Copy the data as there is no concrete ColorModel for // a SinglePixelPackedSampleModel with numBands < 3. tile = tile.createChild(tile.getMinX(), tile.getMinY(), tile.getWidth(), tile.getHeight(), tile.getMinX(), tile.getMinY(), bands); WritableRaster raster = createTile(tileX, tileY); raster.setRect(tile); return raster; } else { // Simply return a child of the corresponding source tile. return tile.createChild(tile.getMinX(), tile.getMinY(), tile.getWidth(), tile.getHeight(), tile.getMinX(), tile.getMinY(), bands); } } public Raster getTile(int tileX, int tileY) { // if we have to perform a copy, caching is needed! if (triggerCopyOperation) { return super.getTile(tileX, tileY); } // Just to return computeTile() result so as to avoid caching. return computeTile(tileX, tileY); } }