private void makeNoDataTransparent( BufferedImage image, short missingDataSignal, short missingDataReplacement) { WritableRaster raster = null; if (null != image && (image.getType() == BufferedImage.TYPE_4BYTE_ABGR || image.getType() == BufferedImage.TYPE_INT_ARGB) && null != (raster = image.getRaster())) { int nodata_r = missingDataSignal & 0xff; int nodata_g = missingDataSignal & 0xff; int nodata_b = missingDataSignal & 0xff; int[] pixel = new int[4]; int[] transparentPixel = new int[] { 0xFF & missingDataReplacement /* red */, 0xFF & missingDataReplacement /* green */, 0xFF & missingDataReplacement, 0x00 /* alpha */ }; int width = image.getWidth(); int height = image.getHeight(); for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { // We know, by the nature of this source, that we are dealing with RGBA rasters... raster.getPixel(i, j, pixel); if (pixel[0] == nodata_r && pixel[1] == nodata_g && pixel[2] == nodata_b) { // pixel[0] = pixel[1] = pixel[2] = 0xFF & // missingDataReplacement; // pixel[3] = 0; // transparent raster.setPixel(i, j, transparentPixel); } else { pixel[3] = 0xff; // non-transparent raster.setPixel(i, j, pixel); } } } } }
private void writeColorImage(BufferedImage image, AVList params) throws IOException { int numBands = image.getRaster().getNumBands(); long offset; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * numBands); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * numBands; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); for (int j = 0; j < numCols * numBands; j++) { putUnsignedByte(dataBuff, rowData[j]); } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add( new TiffIFDEntry( Tiff.Tag.PLANAR_CONFIGURATION, Tiff.Type.SHORT, 1, Tiff.PlanarConfiguration.CHUNKY)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Color_RGB)); ifds.add(new TiffIFDEntry(Tiff.Tag.ORIENTATION, Tiff.Type.SHORT, 1, Tiff.Orientation.DEFAULT)); offset = this.theChannel.position(); short[] bps = new short[numBands]; for (int i = 0; i < numBands; i++) { bps[i] = Tiff.BitsPerSample.MONOCHROME_BYTE; } this.theChannel.write(ByteBuffer.wrap(this.getBytes(bps))); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, numBands, offset)); offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
private void writeGrayscaleImage(BufferedImage image, AVList params) throws IOException { int type = image.getType(); int bitsPerSample = (BufferedImage.TYPE_USHORT_GRAY == type) ? Tiff.BitsPerSample.MONOCHROME_UINT16 : Tiff.BitsPerSample.MONOCHROME_UINT8; int numBands = image.getSampleModel().getNumBands(); // well, numBands for GrayScale images must be 1 int bytesPerSample = numBands * bitsPerSample / Byte.SIZE; this.writeTiffHeader(); // write the image data... int numRows = image.getHeight(); int numCols = image.getWidth(); int[] stripCounts = new int[numRows]; int[] stripOffsets = new int[numRows]; ByteBuffer dataBuff = ByteBuffer.allocateDirect(numCols * bytesPerSample); Raster rast = image.getRaster(); for (int i = 0; i < numRows; i++) { stripOffsets[i] = (int) this.theChannel.position(); stripCounts[i] = numCols * bytesPerSample; int[] rowData = rast.getPixels(0, i, image.getWidth(), 1, (int[]) null); dataBuff.clear(); if (BufferedImage.TYPE_USHORT_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedShort(dataBuff, rowData[j]); } } else if (BufferedImage.TYPE_BYTE_GRAY == type) { for (int j = 0; j < numCols * numBands; j++) { this.putUnsignedByte(dataBuff, rowData[j]); } } dataBuff.flip(); this.theChannel.write(dataBuff); } // write out values for the tiff tags and build up the IFD. These are supposed to be sorted; for // now // do this manually here. ArrayList<TiffIFDEntry> ifds = new ArrayList<TiffIFDEntry>(10); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_WIDTH, Tiff.Type.LONG, 1, numCols)); ifds.add(new TiffIFDEntry(Tiff.Tag.IMAGE_LENGTH, Tiff.Type.LONG, 1, numRows)); ifds.add(new TiffIFDEntry(Tiff.Tag.BITS_PER_SAMPLE, Tiff.Type.SHORT, 1, bitsPerSample)); ifds.add(new TiffIFDEntry(Tiff.Tag.COMPRESSION, Tiff.Type.LONG, 1, Tiff.Compression.NONE)); ifds.add( new TiffIFDEntry( Tiff.Tag.PHOTO_INTERPRETATION, Tiff.Type.SHORT, 1, Tiff.Photometric.Grayscale_BlackIsZero)); ifds.add( new TiffIFDEntry(Tiff.Tag.SAMPLE_FORMAT, Tiff.Type.SHORT, 1, Tiff.SampleFormat.UNSIGNED)); long offset = this.theChannel.position(); dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF); for (int stripOffset : stripOffsets) { dataBuff.putInt(stripOffset); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add(new TiffIFDEntry(Tiff.Tag.STRIP_OFFSETS, Tiff.Type.LONG, stripOffsets.length, offset)); ifds.add(new TiffIFDEntry(Tiff.Tag.SAMPLES_PER_PIXEL, Tiff.Type.SHORT, 1, numBands)); ifds.add(new TiffIFDEntry(Tiff.Tag.ROWS_PER_STRIP, Tiff.Type.LONG, 1, 1)); offset = this.theChannel.position(); dataBuff.clear(); // stripOffsets and stripCounts are same length by design; can reuse the // ByteBuffer... for (int stripCount : stripCounts) { dataBuff.putInt(stripCount); } dataBuff.flip(); this.theChannel.write(dataBuff); ifds.add( new TiffIFDEntry(Tiff.Tag.STRIP_BYTE_COUNTS, Tiff.Type.LONG, stripCounts.length, offset)); this.appendGeoTiff(ifds, params); this.writeIFDs(ifds); }
public void write(BufferedImage image, AVList params) throws IOException { if (image == null) { String msg = Logging.getMessage("nullValue.ImageSource"); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (0 == image.getWidth() || 0 == image.getHeight()) { String msg = Logging.getMessage("generic.InvalidImageSize", image.getWidth(), image.getHeight()); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } if (null == params || 0 == params.getValues().size()) { String reason = Logging.getMessage("nullValue.AVListIsNull"); Logging.logger().finest(Logging.getMessage("GeotiffWriter.GeoKeysMissing", reason)); params = new AVListImpl(); } else { this.validateParameters(params, image.getWidth(), image.getHeight()); } // how we proceed in part depends upon the image type... int type = image.getType(); // handle CUSTOM type which comes from our GeoTiffreader (for now) if (BufferedImage.TYPE_CUSTOM == type) { int numColorComponents = 0, numComponents = 0, pixelSize = 0, dataType = 0, csType = 0; boolean hasAlpha = false; if (null != image.getColorModel()) { ColorModel cm = image.getColorModel(); numColorComponents = cm.getNumColorComponents(); numComponents = cm.getNumComponents(); pixelSize = cm.getPixelSize(); hasAlpha = cm.hasAlpha(); ColorSpace cs = cm.getColorSpace(); if (null != cs) csType = cs.getType(); } if (null != image.getSampleModel()) { SampleModel sm = image.getSampleModel(); dataType = sm.getDataType(); } if (dataType == DataBuffer.TYPE_FLOAT && pixelSize == Float.SIZE && numComponents == 1) { type = BufferedImage_TYPE_ELEVATION_FLOAT32; } else if (dataType == DataBuffer.TYPE_SHORT && pixelSize == Short.SIZE && numComponents == 1) { type = BufferedImage_TYPE_ELEVATION_SHORT16; } else if (ColorSpace.CS_GRAY == csType && pixelSize == Byte.SIZE) { type = BufferedImage.TYPE_BYTE_GRAY; } else if (dataType == DataBuffer.TYPE_USHORT && ColorSpace.CS_GRAY == csType && pixelSize == Short.SIZE) { type = BufferedImage.TYPE_USHORT_GRAY; } else if (ColorSpace.TYPE_RGB == csType && pixelSize == 3 * Byte.SIZE && numColorComponents == 3) { type = BufferedImage.TYPE_3BYTE_BGR; } else if (ColorSpace.TYPE_RGB == csType && hasAlpha && pixelSize == 4 * Byte.SIZE && numComponents == 4) { type = BufferedImage.TYPE_4BYTE_ABGR; } } switch (type) { case BufferedImage.TYPE_3BYTE_BGR: case BufferedImage.TYPE_4BYTE_ABGR: case BufferedImage.TYPE_4BYTE_ABGR_PRE: case BufferedImage.TYPE_INT_RGB: case BufferedImage.TYPE_INT_BGR: case BufferedImage.TYPE_INT_ARGB: case BufferedImage.TYPE_INT_ARGB_PRE: { this.writeColorImage(image, params); } break; case BufferedImage.TYPE_USHORT_GRAY: case BufferedImage.TYPE_BYTE_GRAY: { this.writeGrayscaleImage(image, params); } break; case BufferedImage_TYPE_ELEVATION_SHORT16: case BufferedImage_TYPE_ELEVATION_FLOAT32: { String msg = Logging.getMessage("GeotiffWriter.FeatureNotImplementedd", type); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } // break; case BufferedImage.TYPE_CUSTOM: default: { ColorModel cm = image.getColorModel(); SampleModel sm = image.getSampleModel(); StringBuffer sb = new StringBuffer(Logging.getMessage("GeotiffWriter.UnsupportedType", type)); sb.append("\n"); sb.append("NumBands=").append(sm.getNumBands()).append("\n"); sb.append("NumDataElements=").append(sm.getNumDataElements()).append("\n"); sb.append("NumColorComponents=").append(cm.getNumColorComponents()).append("\n"); sb.append("NumComponents=").append(cm.getNumComponents()).append("\n"); sb.append("PixelSize=").append(cm.getPixelSize()).append("\n"); sb.append("hasAlpha=").append(cm.hasAlpha()); String msg = sb.toString(); Logging.logger().severe(msg); throw new IllegalArgumentException(msg); } } }