geotiffwriter.java

来自「world wind java sdk 源码」· Java 代码 · 共 334 行

JAVA
334
字号
/* Copyright (C) 2001, 2008 United States Government as represented by   the Administrator of the National Aeronautics and Space Administration.   All Rights Reserved. */package gov.nasa.worldwind.formats.tiff;import gov.nasa.worldwind.util.*;import javax.imageio.*;import java.io.*;import java.awt.image.*;import java.nio.channels.*;import java.nio.*;import java.util.*;/** * @author brownrigg * @version $Id$ */public class GeotiffWriter{    private RandomAccessFile targetFile;    private FileChannel theChannel;    // We need the size in bytes of various primitives...    private static final int DOUBLE_SIZEOF = Double.SIZE / Byte.SIZE;    private static final int FLOAT_SIZEOF = Float.SIZE / Byte.SIZE;    private static final int INTEGER_SIZEOF = Integer.SIZE / Byte.SIZE;    private static final int SHORT_SIZEOF = Short.SIZE / Byte.SIZE;    public GeotiffWriter(String filename) throws IOException    {        // the initializer does the validity checking...        commonInitializer(filename);    }    public GeotiffWriter(File targetFile) throws IOException    {        if (targetFile == null)        {            String msg = Logging.getMessage("nullValue.FileIsNull");            Logging.logger().severe(msg);            throw new IllegalArgumentException(msg);        }        commonInitializer(targetFile.getAbsolutePath());    }    //    // Merely consolidates the error checking for the ctors in one place.    //    private void commonInitializer(String targetFilename) throws IOException    {        if (targetFilename == null)        {            String msg = Logging.getMessage("nullValue.FileIsNull");            Logging.logger().severe(msg);            throw new IllegalArgumentException(msg);        }        File file = new File(targetFilename);        if (!file.getParentFile().canWrite())        {            String msg = Logging.getMessage("GeotiffWriter.BadFile", targetFilename);            Logging.logger().severe(msg);            throw new IllegalArgumentException(msg);        }        this.targetFile = new RandomAccessFile(targetFilename, "rw");        this.theChannel = this.targetFile.getChannel();    }    public void close()    {        try        {            this.targetFile.close();        }        catch (Exception ex) { /* best effort */ }    }    public void write(BufferedImage image) throws IOException    {        if (image == null)        {            String msg = Logging.getMessage("nullValue.ImageSource");            Logging.logger().severe(msg);            throw new IllegalArgumentException(msg);        }        // how we proceed in part depends upon the image type...        int type = image.getType();        if (type == BufferedImage.TYPE_3BYTE_BGR || type == BufferedImage.TYPE_4BYTE_ABGR ||            type == BufferedImage.TYPE_4BYTE_ABGR_PRE || type == BufferedImage.TYPE_INT_RGB ||            type == BufferedImage.TYPE_INT_BGR || type == BufferedImage.TYPE_INT_ARGB ||            type == BufferedImage.TYPE_INT_ARGB_PRE)            writeColorImage(image);        else if (type == BufferedImage.TYPE_BYTE_GRAY)            writeGrayscaleImage(image);        else        {            String msg = Logging.getMessage("GeotiffWriter.UnsupportedType", type);            Logging.logger().severe(msg);            throw new IllegalArgumentException(msg);        }    }    private void writeColorImage(BufferedImage image) throws IOException    {        int numBands = image.getRaster().getNumBands();        // write the header...        writeHeader();        // 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(TiffTags.IMAGE_WIDTH, TiffTypes.LONG, 1, numCols));        ifds.add(new TiffIFDEntry(TiffTags.IMAGE_LENGTH, TiffTypes.LONG, 1, numRows));        byte[] tmp = new byte[numBands * 2];        for (int i = 0; i < numBands * 2; i++)        {            tmp[i] = ((i % 2) == 0) ? (byte) 0 : (byte) 8;        }        ByteBuffer buff = ByteBuffer.wrap(tmp);        long offset = this.theChannel.position();        this.theChannel.write(buff);        ifds.add(new TiffIFDEntry(TiffTags.BITS_PER_SAMPLE, TiffTypes.SHORT, numBands, offset));        ifds.add(new TiffIFDEntry(TiffTags.COMPRESSION, TiffTypes.LONG, 1, TiffConstants.COMPRESSION_NONE));        ifds.add(new TiffIFDEntry(TiffTags.PHOTO_INTERPRETATION, TiffTypes.SHORT, 1, TiffConstants.PHOTOINTERP_RGB));        offset = this.theChannel.position();        dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF);        for (int i = 0; i < stripOffsets.length; i++)        {            dataBuff.putInt(stripOffsets[i]);        }        dataBuff.flip();        this.theChannel.write(dataBuff);        ifds.add(new TiffIFDEntry(TiffTags.STRIP_OFFSETS, TiffTypes.LONG, stripOffsets.length, (int) offset));        ifds.add(new TiffIFDEntry(TiffTags.SAMPLES_PER_PIXEL, TiffTypes.SHORT, 1, numBands));        ifds.add(new TiffIFDEntry(TiffTags.ROWS_PER_STRIP, TiffTypes.LONG, 1, 1));        offset = this.theChannel.position();        dataBuff.clear();  // stripOffsets and stripCounts are same length by design; can reuse the ByteBuffer...        for (int i = 0; i < stripCounts.length; i++)        {            dataBuff.putInt(stripCounts[i]);        }        dataBuff.flip();        this.theChannel.write(dataBuff);        ifds.add(new TiffIFDEntry(TiffTags.STRIP_BYTE_COUNTS, TiffTypes.LONG, stripCounts.length, (int) offset));        ifds.add(new TiffIFDEntry(TiffTags.PLANAR_CONFIGURATION, TiffTypes.SHORT, 1,            TiffConstants.PLANARCONFIG_CHUNKY));        writeIFDs(ifds);    }    //    // We only support 8-bit/sample currently (Tiff spec allows for 4 bit/sample).    //    private void writeGrayscaleImage(BufferedImage image) throws IOException    {        int numBands = 1;        // write the header...        writeHeader();        // 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(TiffTags.IMAGE_WIDTH, TiffTypes.LONG, 1, numCols));        ifds.add(new TiffIFDEntry(TiffTags.IMAGE_LENGTH, TiffTypes.LONG, 1, numRows));        ifds.add(new TiffIFDEntry(TiffTags.BITS_PER_SAMPLE, TiffTypes.SHORT, numBands, 8));        ifds.add(new TiffIFDEntry(TiffTags.COMPRESSION, TiffTypes.LONG, 1, TiffConstants.COMPRESSION_NONE));        ifds.add(new TiffIFDEntry(TiffTags.PHOTO_INTERPRETATION, TiffTypes.SHORT, 1,            TiffConstants.PHOTOINTERP_BLACKISZERO));        long offset = this.theChannel.position();        dataBuff = ByteBuffer.allocateDirect(stripOffsets.length * INTEGER_SIZEOF);        for (int i = 0; i < stripOffsets.length; i++)        {            dataBuff.putInt(stripOffsets[i]);        }        dataBuff.flip();        this.theChannel.write(dataBuff);        ifds.add(new TiffIFDEntry(TiffTags.STRIP_OFFSETS, TiffTypes.LONG, stripOffsets.length, (int) offset));        ifds.add(new TiffIFDEntry(TiffTags.SAMPLES_PER_PIXEL, TiffTypes.SHORT, 1, numBands));        ifds.add(new TiffIFDEntry(TiffTags.ROWS_PER_STRIP, TiffTypes.LONG, 1, 1));        offset = this.theChannel.position();        dataBuff.clear();  // stripOffsets and stripCounts are same length by design; can reuse the ByteBuffer...        for (int i = 0; i < stripCounts.length; i++)        {            dataBuff.putInt(stripCounts[i]);        }        dataBuff.flip();        this.theChannel.write(dataBuff);        ifds.add(new TiffIFDEntry(TiffTags.STRIP_BYTE_COUNTS, TiffTypes.LONG, stripCounts.length, (int) offset));        writeIFDs(ifds);    }    private void writeHeader() throws IOException    {        byte[] hdr = new byte[8];        ByteBuffer buff = ByteBuffer.wrap(hdr);        buff.putShort((short) 0x4D4D);  // magic numbers...        buff.put((byte) 0);        buff.put((byte) 42);        buff.putInt(0);                // we'll patch this up later after writing the image...        buff.flip();        this.theChannel.write(buff);    }    private void writeIFDs(ArrayList<TiffIFDEntry> ifds) throws IOException    {        long offset = this.theChannel.position();        // This is supposed to start on a word boundary, via decree of the spec.        long adjust = offset % 4L;        offset += (adjust == 0) ? 0 : (4L - adjust);        this.theChannel.position(offset);        ByteBuffer dataBuff = ByteBuffer.allocateDirect(ifds.size() * 12);        // The IFD directory is preceeded by a SHORT count of the number of entries...        putUnsignedShort(dataBuff, ifds.size());        dataBuff.flip();        this.theChannel.write(dataBuff);        dataBuff.clear();        for (TiffIFDEntry ifd : ifds)        {            putUnsignedShort(dataBuff, ifd.tag);            putUnsignedShort(dataBuff, ifd.type);            putUnsignedInt(dataBuff, ifd.count);            if (ifd.type == TiffTypes.SHORT && ifd.count == 1)            {                // these get packed in the first few bytes...                putUnsignedShort(dataBuff, (int) ifd.valOffset);                dataBuff.putShort((short) 0);            }            else                putUnsignedInt(dataBuff, ifd.valOffset);        }        dataBuff.flip();        this.theChannel.write(dataBuff);        // The spec requires 4 bytes of zeros at the end...        dataBuff.clear();        dataBuff.putInt(0);        dataBuff.flip();        this.theChannel.write(dataBuff);        // go back and patch up the ifd offset in header...        this.theChannel.position(4);        dataBuff.clear();        putUnsignedInt(dataBuff, offset);        dataBuff.flip();        this.theChannel.write(dataBuff);    }    private void putUnsignedByte(ByteBuffer buff, int value)    {        buff.put((byte) (value & 0xff));    }    private void putUnsignedShort(ByteBuffer buff, int value)    {        buff.putShort((short) (value & 0xffff));    }    private void putUnsignedInt(ByteBuffer buff, long value)    {        buff.putInt((int) (value & 0xffffffffL));    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?