Skip to content
Snippets Groups Projects
QCMPFileHeader.java 7.04 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vojtech Moravec's avatar
    Vojtech Moravec committed
    package azgracompress.fileformat;
    
    Vojtech Moravec's avatar
    Vojtech Moravec committed
    import azgracompress.U16;
    
    import azgracompress.compression.VQImageCompressor;
    
    Vojtech Moravec's avatar
    Vojtech Moravec committed
    import azgracompress.data.V3i;
    
    import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.IOException;
    
    
    public class QCMPFileHeader implements Cloneable {
    
        public static final int BASE_QCMP_HEADER_SIZE = 23;
    
        public static final String QCMP_MAGIC_VALUE = "QCMPFILE";
    
    
        private String magicValue = QCMP_MAGIC_VALUE;
    
        private QuantizationType quantizationType;
    
        private byte bitsPerCodebookIndex;
    
        private boolean codebookPerPlane;
    
        private int imageSizeX;
        private int imageSizeY;
        private int imageSizeZ;
    
        private int vectorSizeX;
        private int vectorSizeY;
        private int vectorSizeZ;
    
    
        private long[] planeDataSizes;
    
    
        /**
         * Validate that all header values are in their valid range.
    
         * @return True if this is valid QCMPFILE header.
         */
        public boolean validateHeader() {
            if (!magicValue.equals(QCMP_MAGIC_VALUE))
                return false;
    
    
            if (bitsPerCodebookIndex == 0)
    
                return false;
    
            if (!U16.isInRange(imageSizeX))
                return false;
            if (!U16.isInRange(imageSizeY))
                return false;
            if (!U16.isInRange(imageSizeZ))
                return false;
    
            if (!U16.isInRange(vectorSizeX))
                return false;
            if (!U16.isInRange(vectorSizeY))
                return false;
            if (!U16.isInRange(vectorSizeZ))
                return false;
    
            return true;
        }
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public QCMPFileHeader copyOf() {
            try {
                return (QCMPFileHeader) this.clone();
    
            } catch (final CloneNotSupportedException e) {
    
        public void writeHeader(final DataOutputStream outputStream) throws IOException {
    
            outputStream.writeBytes(QCMP_MAGIC_VALUE);
    
            outputStream.writeByte(quantizationType.getValue());
    
            outputStream.writeByte(bitsPerCodebookIndex);
    
            outputStream.writeBoolean(codebookPerPlane);
    
    
            outputStream.writeShort(imageSizeX);
            outputStream.writeShort(imageSizeY);
            outputStream.writeShort(imageSizeZ);
    
            outputStream.writeShort(vectorSizeX);
            outputStream.writeShort(vectorSizeY);
            outputStream.writeShort(vectorSizeZ);
    
            // NOTE(Moravec): Allocate space for plane/voxel layers data sizes. Offset: 23.
            final int chunkCount = (quantizationType != QuantizationType.Vector3D)
                    ? imageSizeZ
                    : VQImageCompressor.calculateVoxelLayerCount(imageSizeZ, vectorSizeZ);
    
            for (int i = 0; i < chunkCount; i++) {
    
                outputStream.writeInt(0x0);
            }
    
        public boolean readHeader(final DataInputStream inputStream) throws IOException {
    
            if (inputStream.available() < BASE_QCMP_HEADER_SIZE) {
    
            final byte[] magicBuffer = new byte[QCMP_MAGIC_VALUE.length()];
    
            int toRead = QCMP_MAGIC_VALUE.length();
            while (toRead > 0) {
                final int read = inputStream.read(magicBuffer, QCMP_MAGIC_VALUE.length() - toRead, toRead);
                if (read < 0) {
                    // Invalid magic value.
                    return false;
                }
                toRead -= read;
    
    Vojtech Moravec's avatar
    Vojtech Moravec committed
    
            magicValue = new String(magicBuffer);
    
            if (!magicValue.equals(QCMP_MAGIC_VALUE)) {
                return false;
            }
    
    
            quantizationType = QuantizationType.fromByte(inputStream.readByte());
    
            bitsPerCodebookIndex = inputStream.readByte();
    
            codebookPerPlane = inputStream.readBoolean();
    
    
            imageSizeX = inputStream.readUnsignedShort();
            imageSizeY = inputStream.readUnsignedShort();
            imageSizeZ = inputStream.readUnsignedShort();
    
            vectorSizeX = inputStream.readUnsignedShort();
            vectorSizeY = inputStream.readUnsignedShort();
            vectorSizeZ = inputStream.readUnsignedShort();
    
    
            final int chunkCount = (quantizationType != QuantizationType.Vector3D)
                    ? imageSizeZ
                    : VQImageCompressor.calculateVoxelLayerCount(imageSizeZ, vectorSizeZ);
    
            planeDataSizes = new long[chunkCount];
            for (int i = 0; i < chunkCount; i++) {
    
                final long readValue = inputStream.readInt();
                planeDataSizes[i] = (readValue & 0x00000000FFFFFFFFL);
            }
    
    
    
        public QuantizationType getQuantizationType() {
            return quantizationType;
        }
    
    
        public void setQuantizationType(final QuantizationType quantizationType) {
    
            this.quantizationType = quantizationType;
        }
    
    
        public byte getBitsPerCodebookIndex() {
            return bitsPerCodebookIndex;
    
        public void setBitsPerCodebookIndex(final byte bitsPerCodebookIndex) {
    
            this.bitsPerCodebookIndex = bitsPerCodebookIndex;
    
        }
    
        public boolean isCodebookPerPlane() {
            return codebookPerPlane;
        }
    
    
        public void setCodebookPerPlane(final boolean codebookPerPlane) {
    
            this.codebookPerPlane = codebookPerPlane;
        }
    
        public int getImageSizeX() {
            return imageSizeX;
        }
    
    
        public void setImageSizeX(final int imageSizeX) {
    
            this.imageSizeX = imageSizeX;
        }
    
        public int getImageSizeY() {
            return imageSizeY;
        }
    
    
        public void setImageSizeY(final int imageSizeY) {
    
            this.imageSizeY = imageSizeY;
        }
    
        public int getImageSizeZ() {
            return imageSizeZ;
    
        }
    
        public V3i getImageDims() {
            return new V3i(imageSizeX, imageSizeY, imageSizeZ);
    
        public void setImageSizeZ(final int imageSizeZ) {
    
            this.imageSizeZ = imageSizeZ;
        }
    
        public int getVectorSizeX() {
            return vectorSizeX;
        }
    
    
        public void setVectorSizeX(final int vectorSizeX) {
    
            this.vectorSizeX = vectorSizeX;
        }
    
        public int getVectorSizeY() {
            return vectorSizeY;
        }
    
    
        public void setVectorSizeY(final int vectorSizeY) {
    
            this.vectorSizeY = vectorSizeY;
        }
    
        public int getVectorSizeZ() {
            return vectorSizeZ;
        }
    
    
        public void setVectorSizeZ(final int vectorSizeZ) {
    
            this.vectorSizeZ = vectorSizeZ;
        }
    
    
        public String getMagicValue() {
            return magicValue;
        }
    
    
        public void setImageDimension(final V3i imageDims) {
            imageSizeX = imageDims.getX();
            imageSizeY = imageDims.getY();
            imageSizeZ = imageDims.getZ();
        }
    
    
        public void setVectorDimension(final V3i vectorDims) {
    
            vectorSizeX = vectorDims.getX();
            vectorSizeY = vectorDims.getY();
    
    
        public long[] getPlaneDataSizes() {
            return planeDataSizes;
        }
    
    
        public void setPlaneDataSizes(final long[] sizes) {
            planeDataSizes = sizes;
        }
    
    
        public long getHeaderSize() {
    
            final int chunkCount = (quantizationType != QuantizationType.Vector3D)
                    ? imageSizeZ
                    : VQImageCompressor.calculateVoxelLayerCount(imageSizeZ, vectorSizeZ);
    
            return BASE_QCMP_HEADER_SIZE + (chunkCount * 4);