/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.beam.dataio.smos; import com.bc.ceres.glevel.MultiLevelModel; import com.bc.ceres.jai.NoDataRaster; import org.esa.beam.framework.datamodel.RasterDataNode; import org.esa.beam.jai.ImageManager; import org.esa.beam.jai.ResolutionLevel; import org.esa.beam.jai.SingleBandedOpImage; import org.esa.beam.smos.dgg.SmosDgg; import javax.media.jai.PixelAccessor; import javax.media.jai.PlanarImage; import javax.media.jai.UnpackedImageData; import java.awt.Rectangle; import java.awt.geom.Area; import java.awt.image.ColorModel; import java.awt.image.DataBuffer; import java.awt.image.Raster; import java.awt.image.RenderedImage; import java.awt.image.WritableRaster; import java.util.Arrays; class SmosOpImage extends SingleBandedOpImage { private final ValueProvider valueProvider; private final MultiLevelModel model; private final double noDataValue; private volatile Area area; private volatile NoDataRaster noDataRaster; SmosOpImage(ValueProvider valueProvider, RasterDataNode rasterDataNode, MultiLevelModel model, ResolutionLevel level) { super(ImageManager.getDataBufferType(rasterDataNode.getDataType()), rasterDataNode.getSceneRasterWidth(), rasterDataNode.getSceneRasterHeight(), rasterDataNode.getProduct().getPreferredTileSize(), null, // no configuration level); this.valueProvider = valueProvider; this.model = model; this.noDataValue = rasterDataNode.getNoDataValue(); } private Area getArea() { if (area == null) { synchronized (this) { if (area == null) { final Area modelArea = valueProvider.getArea(); area = modelArea.createTransformedArea(model.getModelToImageTransform(getLevel())); } } } return area; } @Override public Raster computeTile(int tileX, int tileY) { if (getArea().intersects(getTileRect(tileX, tileY))) { return super.computeTile(tileX, tileY); } if (noDataRaster == null) { synchronized (this) { if (noDataRaster == null) { noDataRaster = createNoDataRaster(noDataValue); } } } return noDataRaster.createTranslatedChild(tileXToX(tileX), tileYToY(tileY)); } @Override protected final void computeRect(PlanarImage[] planarImages, WritableRaster targetRaster, Rectangle rectangle) { final RenderedImage seqnumImage = SmosDgg.getInstance().getMultiLevelImage().getImage(getLevel()); final Raster seqnumRaster = seqnumImage.getData(rectangle); final ColorModel cm = getColorModel(); final PixelAccessor seqnumAccessor = new PixelAccessor(seqnumRaster.getSampleModel(), cm); final PixelAccessor targetAccessor = new PixelAccessor(targetRaster.getSampleModel(), null); final UnpackedImageData seqnumData = seqnumAccessor.getPixels( seqnumRaster, rectangle, seqnumRaster.getSampleModel().getTransferType(), false); final UnpackedImageData targetData = targetAccessor.getPixels( targetRaster, rectangle, targetRaster.getSampleModel().getTransferType(), true); final PixelCounter pixelCounter = new PixelCounter(rectangle, getArea()); switch (targetData.type) { case DataBuffer.TYPE_BYTE: byteLoop(seqnumData, targetData, pixelCounter, (byte) noDataValue); break; case DataBuffer.TYPE_SHORT: case DataBuffer.TYPE_USHORT: shortLoop(seqnumData, targetData, pixelCounter, (short) noDataValue); break; case DataBuffer.TYPE_INT: intLoop(seqnumData, targetData, pixelCounter, (int) noDataValue); break; case DataBuffer.TYPE_FLOAT: floatLoop(seqnumData, targetData, pixelCounter, (float) noDataValue); break; default: // do nothing break; } targetAccessor.setPixels(targetData); } private void byteLoop(UnpackedImageData seqnumData, UnpackedImageData targetData, PixelCounter pixelCounter, byte noDataValue) { final int w = targetData.rect.width; final int h = targetData.rect.height; final int seqnumPixelStride = seqnumData.pixelStride; final int seqnumLineStride = seqnumData.lineStride; final int[] seqnumDataArray = seqnumData.getIntData(0); final int targetPixelStride = targetData.pixelStride; final int targetLineStride = targetData.lineStride; final byte[] targetDataArray = targetData.getByteData(0); final int[] seqnumCache = new int[w]; final byte[] valueCache = new byte[w]; int seqnumLineOffset = seqnumData.getOffset(0); int targetLineOffset = targetData.getOffset(0); for (int y = 0; y < h; ++y) { int seqnumPixelOffset = seqnumLineOffset; int targetPixelOffset = targetLineOffset; pixelCounter.countPixels(targetData.rect.y + y); if (pixelCounter.leading > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.leading, noDataValue); targetPixelOffset += pixelCounter.leading * targetPixelStride; seqnumPixelOffset += pixelCounter.leading * seqnumPixelStride; } if (pixelCounter.valid > 0) { for (int x = pixelCounter.leading; x < pixelCounter.leading + pixelCounter.valid; ++x) { byte value; final int seqnum = seqnumDataArray[seqnumPixelOffset]; if (x > 0 && seqnumCache[x - 1] == seqnum) { // pixel to the west value = valueCache[x - 1]; } else if (y > 0 && seqnumCache[x] == seqnum) { // pixel to the north value = valueCache[x]; } else if (x + 1 < w && y > 0 && seqnumCache[x + 1] == seqnum) { // pixel to the north-east value = valueCache[x + 1]; } else { value = valueProvider.getValue(seqnum, noDataValue); } seqnumCache[x] = seqnum; valueCache[x] = value; targetDataArray[targetPixelOffset] = value; seqnumPixelOffset += seqnumPixelStride; targetPixelOffset += targetPixelStride; } } if (pixelCounter.trailing > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.trailing, noDataValue); } seqnumLineOffset += seqnumLineStride; targetLineOffset += targetLineStride; } } private void shortLoop(UnpackedImageData seqnumData, UnpackedImageData targetData, PixelCounter pixelCounter, short noDataValue) { final int w = targetData.rect.width; final int h = targetData.rect.height; final int seqnumPixelStride = seqnumData.pixelStride; final int seqnumLineStride = seqnumData.lineStride; final int[] seqnumDataArray = seqnumData.getIntData(0); final int targetPixelStride = targetData.pixelStride; final int targetLineStride = targetData.lineStride; final short[] targetDataArray = targetData.getShortData(0); final int[] seqnumCache = new int[w]; final short[] valueCache = new short[w]; int seqnumLineOffset = seqnumData.getOffset(0); int targetLineOffset = targetData.getOffset(0); for (int y = 0; y < h; ++y) { int seqnumPixelOffset = seqnumLineOffset; int targetPixelOffset = targetLineOffset; pixelCounter.countPixels(targetData.rect.y + y); if (pixelCounter.leading > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.leading, noDataValue); targetPixelOffset += pixelCounter.leading * targetPixelStride; seqnumPixelOffset += pixelCounter.leading * seqnumPixelStride; } if (pixelCounter.valid > 0) { for (int x = pixelCounter.leading; x < pixelCounter.leading + pixelCounter.valid; ++x) { short value; final int seqnum = seqnumDataArray[seqnumPixelOffset]; if (x > 0 && seqnumCache[x - 1] == seqnum) { // pixel to the west value = valueCache[x - 1]; } else if (y > 0 && seqnumCache[x] == seqnum) { // pixel to the north value = valueCache[x]; } else if (x + 1 < w && y > 0 && seqnumCache[x + 1] == seqnum) { // pixel to the north-east value = valueCache[x + 1]; } else { value = valueProvider.getValue(seqnum, noDataValue); } seqnumCache[x] = seqnum; valueCache[x] = value; targetDataArray[targetPixelOffset] = value; seqnumPixelOffset += seqnumPixelStride; targetPixelOffset += targetPixelStride; } } if (pixelCounter.trailing > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.trailing, noDataValue); } seqnumLineOffset += seqnumLineStride; targetLineOffset += targetLineStride; } } private void intLoop(UnpackedImageData seqnumData, UnpackedImageData targetData, PixelCounter pixelCounter, int noDataValue) { final int w = targetData.rect.width; final int h = targetData.rect.height; final int seqnumPixelStride = seqnumData.pixelStride; final int seqnumLineStride = seqnumData.lineStride; final int[] seqnumDataArray = seqnumData.getIntData(0); final int targetPixelStride = targetData.pixelStride; final int targetLineStride = targetData.lineStride; final int[] targetDataArray = targetData.getIntData(0); final int[] seqnumCache = new int[w]; final int[] valueCache = new int[w]; int seqnumLineOffset = seqnumData.getOffset(0); int targetLineOffset = targetData.getOffset(0); for (int y = 0; y < h; ++y) { int seqnumPixelOffset = seqnumLineOffset; int targetPixelOffset = targetLineOffset; pixelCounter.countPixels(targetData.rect.y + y); if (pixelCounter.leading > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.leading, noDataValue); targetPixelOffset += pixelCounter.leading * targetPixelStride; seqnumPixelOffset += pixelCounter.leading * seqnumPixelStride; } if (pixelCounter.valid > 0) { for (int x = pixelCounter.leading; x < pixelCounter.leading + pixelCounter.valid; ++x) { int value; final int seqnum = seqnumDataArray[seqnumPixelOffset]; if (x > 0 && seqnumCache[x - 1] == seqnum) { // pixel to the west value = valueCache[x - 1]; } else if (y > 0 && seqnumCache[x] == seqnum) { // pixel to the north value = valueCache[x]; } else if (x + 1 < w && y > 0 && seqnumCache[x + 1] == seqnum) { // pixel to the north-east value = valueCache[x + 1]; } else { value = valueProvider.getValue(seqnum, noDataValue); } seqnumCache[x] = seqnum; valueCache[x] = value; targetDataArray[targetPixelOffset] = value; seqnumPixelOffset += seqnumPixelStride; targetPixelOffset += targetPixelStride; } } if (pixelCounter.trailing > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.trailing, noDataValue); } seqnumLineOffset += seqnumLineStride; targetLineOffset += targetLineStride; } } private void floatLoop(UnpackedImageData seqnumData, UnpackedImageData targetData, PixelCounter pixelCounter, float noDataValue) { final int w = targetData.rect.width; final int h = targetData.rect.height; final int seqnumPixelStride = seqnumData.pixelStride; final int seqnumLineStride = seqnumData.lineStride; final int[] seqnumDataArray = seqnumData.getIntData(0); final int targetPixelStride = targetData.pixelStride; final int targetLineStride = targetData.lineStride; final float[] targetDataArray = targetData.getFloatData(0); final int[] seqnumCache = new int[w]; final float[] valueCache = new float[w]; int seqnumLineOffset = seqnumData.getOffset(0); int targetLineOffset = targetData.getOffset(0); for (int y = 0; y < h; ++y) { int seqnumPixelOffset = seqnumLineOffset; int targetPixelOffset = targetLineOffset; pixelCounter.countPixels(targetData.rect.y + y); if (pixelCounter.leading > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.leading, noDataValue); targetPixelOffset += pixelCounter.leading * targetPixelStride; seqnumPixelOffset += pixelCounter.leading * seqnumPixelStride; } if (pixelCounter.valid > 0) { for (int x = pixelCounter.leading; x < pixelCounter.leading + pixelCounter.valid; ++x) { float value; final int seqnum = seqnumDataArray[seqnumPixelOffset]; if (x > 0 && seqnumCache[x - 1] == seqnum) { // pixel to the west value = valueCache[x - 1]; } else if (y > 0 && seqnumCache[x] == seqnum) { // pixel to the north value = valueCache[x]; } else if (x + 1 < w && y > 0 && seqnumCache[x + 1] == seqnum) { // pixel to the north-east value = valueCache[x + 1]; } else { value = valueProvider.getValue(seqnum, noDataValue); } seqnumCache[x] = seqnum; valueCache[x] = value; targetDataArray[targetPixelOffset] = value; seqnumPixelOffset += seqnumPixelStride; targetPixelOffset += targetPixelStride; } } if (pixelCounter.trailing > 0) { Arrays.fill(targetDataArray, targetPixelOffset, targetPixelOffset + pixelCounter.trailing, noDataValue); } seqnumLineOffset += seqnumLineStride; targetLineOffset += targetLineStride; } } private static class PixelCounter { private final Rectangle effectiveBounds; private final Rectangle targetRectangle; private int leading; private int valid; private int trailing; PixelCounter(Rectangle targetRectangle, Area envelope) { final Area effectiveEnvelope = new Area(targetRectangle); effectiveEnvelope.intersect(envelope); this.effectiveBounds = effectiveEnvelope.getBounds(); this.targetRectangle = targetRectangle; } void countPixels(int y) { if (y < effectiveBounds.y || y > effectiveBounds.y + effectiveBounds.height) { leading = targetRectangle.width; valid = 0; trailing = 0; } else { leading = effectiveBounds.x - targetRectangle.x; valid = effectiveBounds.width; trailing = targetRectangle.width - leading - valid; } } } }