Blame wrapping/java/openjp2/java-sources/org/openJpeg/OpenJPEGJavaEncoder.java

Packit caffb5
/*
Packit caffb5
 * Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
Packit caffb5
 * Copyright (c) 2002-2014, Professor Benoit Macq
Packit caffb5
 * Copyright (c) 2002-2007, Patrick Piscaglia, Telemis s.a.
Packit caffb5
 * All rights reserved.
Packit caffb5
 *
Packit caffb5
 * Redistribution and use in source and binary forms, with or without
Packit caffb5
 * modification, are permitted provided that the following conditions
Packit caffb5
 * are met:
Packit caffb5
 * 1. Redistributions of source code must retain the above copyright
Packit caffb5
 *    notice, this list of conditions and the following disclaimer.
Packit caffb5
 * 2. Redistributions in binary form must reproduce the above copyright
Packit caffb5
 *    notice, this list of conditions and the following disclaimer in the
Packit caffb5
 *    documentation and/or other materials provided with the distribution.
Packit caffb5
 *
Packit caffb5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
Packit caffb5
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit caffb5
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit caffb5
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
Packit caffb5
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
Packit caffb5
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
Packit caffb5
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
Packit caffb5
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
Packit caffb5
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
Packit caffb5
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Packit caffb5
 * POSSIBILITY OF SUCH DAMAGE.
Packit caffb5
 */  
Packit caffb5
package org.openJpeg;
Packit caffb5

Packit caffb5
import java.io.File;
Packit caffb5
import java.util.Vector;
Packit caffb5

Packit caffb5
/** This class encodes one image into the J2K format, 
Packit caffb5
 * using the OpenJPEG.org library.
Packit caffb5
 * To be able to log messages, the called must register a IJavaJ2KEncoderLogger object.
Packit caffb5
 */
Packit caffb5
public class OpenJPEGJavaEncoder {
Packit caffb5

Packit caffb5
	public interface IJavaJ2KEncoderLogger {
Packit caffb5
		public void logEncoderMessage(String message);
Packit caffb5
		public void logEncoderError(String message);
Packit caffb5
	}
Packit caffb5
	
Packit caffb5
    private static boolean isInitialized = false;
Packit caffb5
    
Packit caffb5
	// ===== Compression parameters =============>
Packit caffb5
	// These value may be changed for each image
Packit caffb5
    private String[] encoder_arguments = null;
Packit caffb5
	/** number of resolutions decompositions */
Packit caffb5
	private int nbResolutions = -1;
Packit caffb5
	/** the quality layers, expressed as compression rate */
Packit caffb5
	private float[] ratioLayers = null;
Packit caffb5
	/** the quality layers, expressed as PSNR values. This variable, if defined, has priority over the ratioLayers variable */
Packit caffb5
	private float[] psnrLayers = null;
Packit caffb5
	
Packit caffb5
	/** Contains the 8 bpp version of the image. May NOT be filled together with image16 or image24.

Packit caffb5
	 * We store the 8 or 16 bpp version of the original image while the encoder uses a 32 bpp version, because 
Packit caffb5
	 * 
  • the storage capacity required is smaller
  • Packit caffb5
    	 * 
  • the transfer Java --> C will be faster
  • Packit caffb5
    	 * 
  • the conversion byte/short ==> int will be done faster by the C
  • Packit caffb5
    	 * */
    
    Packit caffb5
    	private byte[] image8 = null;
    
    Packit caffb5
    	/** Contains the 16 bpp version of the image. May NOT be filled together with image8 or image24*/
    
    Packit caffb5
    	private short[] image16 = null;
    
    Packit caffb5
    	/** Contains the 24 bpp version of the image. May NOT be filled together with image8 or image16 */
    
    Packit caffb5
    	private int[] image24 = null;
    
    Packit caffb5
    	/** Holds the result of the compression, i.e. the J2K compressed bytecode */
    
    Packit caffb5
        private byte compressedStream[] = null;
    
    Packit caffb5
        /** Holds the compressed stream length, which may be smaller than compressedStream.length if this byte[] is pre-allocated */
    
    Packit caffb5
        private long compressedStreamLength = -1;
    
    Packit caffb5
        /** Holds the compressed version of the index file, returned by the encoder */
    
    Packit caffb5
        private byte compressedIndex[] = null;
    
    Packit caffb5
        /** Width and Height of the image */
    
    Packit caffb5
        private int width = -1;
    
    Packit caffb5
        private int height = -1;
    
    Packit caffb5
        private int depth = -1;
    
    Packit caffb5
        /** Tile size. We suppose the same size for the horizontal and vertical tiles.
    
    Packit caffb5
         * If size == -1 ==> no tiling */
    
    Packit caffb5
        private int tileSize = -1;
    
    Packit caffb5
        // <===== Compression parameters =============
    
    Packit caffb5
        
    
    Packit caffb5
        private Vector<IJavaJ2KEncoderLogger> loggers = new Vector();
    
    Packit caffb5
    
    
    Packit caffb5
        public OpenJPEGJavaEncoder(String openJPEGlibraryFullPathAndName, IJavaJ2KEncoderLogger messagesAndErrorsLogger) throws ExceptionInInitializerError
    
    Packit caffb5
        {
    
    Packit caffb5
        	this(openJPEGlibraryFullPathAndName);
    
    Packit caffb5
        	loggers.addElement(messagesAndErrorsLogger);
    
    Packit caffb5
        }
    
    Packit caffb5
    
    
    Packit caffb5
        public OpenJPEGJavaEncoder(String openJPEGlibraryFullPathAndName) throws ExceptionInInitializerError
    
    Packit caffb5
        {
    
    Packit caffb5
        	if (!isInitialized) {
    
    Packit caffb5
        		try {
    
    Packit caffb5
        			String absolutePath = (new File(openJPEGlibraryFullPathAndName)).getCanonicalPath();
    
    Packit caffb5
        			System.load(absolutePath);
    
    Packit caffb5
        			isInitialized = true;
    
    Packit caffb5
        		} catch (Throwable t) {
    
    Packit caffb5
        			t.printStackTrace();
    
    Packit caffb5
        			throw new ExceptionInInitializerError("OpenJPEG Java Encoder: probably impossible to find the C library");
    
    Packit caffb5
        		}
    
    Packit caffb5
        	}
    
    Packit caffb5
        }
    
    Packit caffb5
        
    
    Packit caffb5
        public void addLogger(IJavaJ2KEncoderLogger messagesAndErrorsLogger) {
    
    Packit caffb5
        	loggers.addElement(messagesAndErrorsLogger);
    
    Packit caffb5
        }
    
    Packit caffb5
        
    
    Packit caffb5
        public void removeLogger(IJavaJ2KEncoderLogger messagesAndErrorsLogger) {
    
    Packit caffb5
        	loggers.removeElement(messagesAndErrorsLogger);
    
    Packit caffb5
        }
    
    Packit caffb5
        
    
    Packit caffb5
        /** This method compresses the given image.

    Packit caffb5
         * It returns the compressed J2K codestream into the compressedStream byte[].

    Packit caffb5
         * It also returns the compression index as a compressed form, into the compressedIndex byte[].

    Packit caffb5
         * One of the image8, image16 or image24 arrays must be correctly initialized and filled.

    Packit caffb5
         * The width, height and depth variables must be correctly filled.

    Packit caffb5
         * The nbResolutions, nbLayers and if needed the float[] psnrLayers or ratioLayers must also be filled before calling this method.
    
    Packit caffb5
         */
    
    Packit caffb5
        public void encodeImageToJ2K() {
    
    Packit caffb5
    		// Need to allocate / reallocate the compressed stream buffer ? (size = max possible size = original image size)
    
    Packit caffb5
    		if (compressedStream== null || (compressedStream.length != width*height*depth/8)) {
    
    Packit caffb5
    			logMessage("OpenJPEGJavaEncoder.encodeImageToJ2K: (re-)allocating " + (width*height*depth/8) + " bytes for the compressedStream");
    
    Packit caffb5
    			compressedStream = new byte[width*height*depth/8];
    
    Packit caffb5
    		}
    
    Packit caffb5
    		// Arguments = 
    
    Packit caffb5
    		// - number of resolutions "-n 5" : 2
    
    Packit caffb5
    		// - size of tile "-t 512,512" : 2
    
    Packit caffb5
    		// 
    
    Packit caffb5
    		// Image width, height, depth and pixels are directly fetched by C from the Java class
    
    Packit caffb5
    		int nbArgs = 2 + (tileSize == -1 ? 0 : 2) + (encoder_arguments != null ? encoder_arguments.length : 0);
    
    Packit caffb5
    		if (psnrLayers != null && psnrLayers.length>0 && psnrLayers[0] != 0)
    
    Packit caffb5
    			// If psnrLayers is defined and doesn't just express "lossless"
    
    Packit caffb5
    			nbArgs += 2;
    
    Packit caffb5
    		else if (ratioLayers != null && ratioLayers.length>0 && ratioLayers[0]!=0.0)
    
    Packit caffb5
    			nbArgs += 2;
    
    Packit caffb5
    		String[] arguments = new String[nbArgs];
    
    Packit caffb5
    		int offset = 0;
    
    Packit caffb5
    		arguments[offset] = "-n"; arguments[offset+1] = "" + nbResolutions; offset += 2;
    
    Packit caffb5
    		if (tileSize!= -1) {
    
    Packit caffb5
    			arguments[offset++] = "-t"; 
    
    Packit caffb5
    			arguments[offset++] = "" + tileSize + "," + tileSize;
    
    Packit caffb5
    		}
    
    Packit caffb5
    		// If PSNR layers are defined, use them to encode the images
    
    Packit caffb5
    		if (psnrLayers != null && psnrLayers.length>0 && psnrLayers[0]!=-1) {
    
    Packit caffb5
    			arguments[offset++] = "-q";
    
    Packit caffb5
    			String s = "";
    
    Packit caffb5
    			for (int i=0; i
    Packit caffb5
    				s += psnrLayers[i] + ",";
    
    Packit caffb5
    			arguments[offset++] = s.substring(0, s.length()-1);
    
    Packit caffb5
    		} else if (ratioLayers != null && ratioLayers.length>0 && ratioLayers[0]!=0.0) {
    
    Packit caffb5
    			// Specify quality ratioLayers, as compression ratios
    
    Packit caffb5
    			arguments[offset++] = "-r";
    
    Packit caffb5
    			String s = "";
    
    Packit caffb5
    			for (int i=0; i
    Packit caffb5
    				s += ratioLayers[i] + ",";
    
    Packit caffb5
    			arguments[offset++] = s.substring(0, s.length()-1);
    
    Packit caffb5
    		}
    
    Packit caffb5
    		if (encoder_arguments != null) {
    
    Packit caffb5
    			for (int i=0; i
    Packit caffb5
    				arguments[i+offset] = encoder_arguments[i];
    
    Packit caffb5
    			}
    
    Packit caffb5
    		}
    
    Packit caffb5
    		logMessage("Encoder additional arguments = " + arrayToString(arguments));
    
    Packit caffb5
    		long startTime = (new java.util.Date()).getTime();
    
    Packit caffb5
    		compressedStreamLength = internalEncodeImageToJ2K(arguments);
    
    Packit caffb5
    		logMessage("compression time = " + ((new java.util.Date()).getTime() - startTime) + " msec");
    
    Packit caffb5
        }
    
    Packit caffb5
        
    
    Packit caffb5
        /** 
    
    Packit caffb5
         * Fills the compressedStream byte[] and the compressedIndex byte[]
    
    Packit caffb5
         * @return the codestream length.
    
    Packit caffb5
         */
    
    Packit caffb5
        private native long internalEncodeImageToJ2K(String[] parameters);
    
    Packit caffb5
    
    
    Packit caffb5
        /** Image depth in bpp */
    
    Packit caffb5
    	public int getDepth() {
    
    Packit caffb5
    		return depth;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
        /** Image depth in bpp */
    
    Packit caffb5
    	public void setDepth(int depth) {
    
    Packit caffb5
    		this.depth = depth;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Image height in pixels  */
    
    Packit caffb5
    	public int getHeight() {
    
    Packit caffb5
    		return height;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Image height in pixels  */
    
    Packit caffb5
    	public void setHeight(int height) {
    
    Packit caffb5
    		this.height = height;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** This method must be called in depth in [9,16].
    
    Packit caffb5
    	 * @param an array of shorts, containing width*height values
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public void setImage16(short[] image16) {
    
    Packit caffb5
    		this.image16 = image16;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** This method must be called in depth in [17,24] for RGB images.
    
    Packit caffb5
    	 * @param an array of int, containing width*height values
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public void setImage24(int[] image24) {
    
    Packit caffb5
    		this.image24 = image24;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** This method must be called in depth in [1,8].
    
    Packit caffb5
    	 * @param an array of bytes, containing width*height values
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public void setImage8(byte[] image8) {
    
    Packit caffb5
    		this.image8 = image8;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Return the ratioLayers, i.e. the compression ratio for each quality layer.
    
    Packit caffb5
    	 * If the last value is 0.0, last layer is lossless compressed.
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public float[] getRatioLayers() {
    
    Packit caffb5
    		return ratioLayers;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/**
    
    Packit caffb5
    	 * sets the quality layers.
    
    Packit caffb5
    	 * At least one level.
    
    Packit caffb5
    	 * Each level is expressed as a compression ratio (float).
    
    Packit caffb5
    	 * If the last value is 0.0, the last layer will be losslessly compressed
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public void setRatioLayers(float[] layers) {
    
    Packit caffb5
    		this.ratioLayers = layers;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Return the PSNR Layers, i.e. the target PSNR for each quality layer.
    
    Packit caffb5
    	 * If the last value is -1, last layer is lossless compressed.
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public float[] getPsnrLayers() {
    
    Packit caffb5
    		return psnrLayers;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/**
    
    Packit caffb5
    	 * sets the quality layers.
    
    Packit caffb5
    	 * At least one level.
    
    Packit caffb5
    	 * Each level is expressed as a target PSNR (float).
    
    Packit caffb5
    	 * If the last value is -1, the last layer will be losslessly compressed
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public void setPsnrLayers(float[] layers) {
    
    Packit caffb5
    		this.psnrLayers = layers;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Set the number of resolutions that must be created */
    
    Packit caffb5
    	public void setNbResolutions(int nbResolutions) {
    
    Packit caffb5
    		this.nbResolutions = nbResolutions;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public int getWidth() {
    
    Packit caffb5
    		return width;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Width of the image, in pixels */
    
    Packit caffb5
    	public void setWidth(int width) {
    
    Packit caffb5
    		this.width = width;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	/** Return the compressed index file.
    
    Packit caffb5
    	 * Syntax: TODO PP:
    
    Packit caffb5
    	 */
    
    Packit caffb5
    	public byte[] getCompressedIndex() {
    
    Packit caffb5
    		return compressedIndex;
    
    Packit caffb5
    	}
    
    Packit caffb5
    	
    
    Packit caffb5
    	public void setCompressedIndex(byte[] index) {
    
    Packit caffb5
    		compressedIndex = index;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public byte[] getCompressedStream() {
    
    Packit caffb5
    		return compressedStream;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public void reset() {
    
    Packit caffb5
    		nbResolutions = -1;
    
    Packit caffb5
    		ratioLayers = null;
    
    Packit caffb5
    		psnrLayers = null;
    
    Packit caffb5
    		image8 = null;
    
    Packit caffb5
    		image16 = null;
    
    Packit caffb5
    		image24 = null;
    
    Packit caffb5
    		compressedStream = null;
    
    Packit caffb5
    	    compressedIndex = null;
    
    Packit caffb5
    	    width = -1;
    
    Packit caffb5
    	    height = -1;
    
    Packit caffb5
    	    depth = -1;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public short[] getImage16() {
    
    Packit caffb5
    		return image16;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public int[] getImage24() {
    
    Packit caffb5
    		return image24;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public byte[] getImage8() {
    
    Packit caffb5
    		return image8;
    
    Packit caffb5
    	}
    
    Packit caffb5
    	
    
    Packit caffb5
    	/** Sets the size of the tiles. We assume square tiles */
    
    Packit caffb5
    	public void setTileSize(int tileSize) {
    
    Packit caffb5
    		this.tileSize = tileSize;
    
    Packit caffb5
    	}
    
    Packit caffb5
    	
    
    Packit caffb5
    	/** Contains all the encoding arguments other than the input/output file, compression ratio, tile size */
    
    Packit caffb5
    	public void setEncoderArguments(String[] argumentsForTheEncoder) {
    
    Packit caffb5
    		encoder_arguments = argumentsForTheEncoder;
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public void logMessage(String message) {
    
    Packit caffb5
    		for (IJavaJ2KEncoderLogger logger:loggers)
    
    Packit caffb5
    			logger.logEncoderMessage(message);
    
    Packit caffb5
    	}
    
    Packit caffb5
    	
    
    Packit caffb5
    	public void logError(String error) {
    
    Packit caffb5
    		for (IJavaJ2KEncoderLogger logger:loggers)
    
    Packit caffb5
    			logger.logEncoderError(error);
    
    Packit caffb5
    	}
    
    Packit caffb5
    
    
    Packit caffb5
    	public long getCompressedStreamLength() {
    
    Packit caffb5
    		return compressedStreamLength;
    
    Packit caffb5
    	}
    
    Packit caffb5
    	
    
    Packit caffb5
    	private String arrayToString(String[] array) {
    
    Packit caffb5
    		if (array == null)
    
    Packit caffb5
    			return "NULL";
    
    Packit caffb5
    		StringBuffer sb = new StringBuffer();
    
    Packit caffb5
    		for (int i=0; i
    Packit caffb5
    			sb.append(array[i]).append(" ");
    
    Packit caffb5
    		sb.delete(sb.length()-1, sb.length());
    
    Packit caffb5
    		return sb.toString();
    
    Packit caffb5
    	}
    
    Packit caffb5
    }