| /* |
| * Copyright (c) 2014-2021 by Wen Yu |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the Eclipse |
| * Public License, v. 2.0 are satisfied: GNU General Public License, version 2 |
| * or any later version. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later |
| * |
| * Change History - most recent changes go on top of previous changes |
| * |
| * ArrayUtils.java |
| * |
| * Who Date Description |
| * ==== ========= ====================================================================== |
| * WY 14Jun2015 Bug fix for toNBits() to use long data type internally |
| * WY 04Jun2015 Rewrote all concatenation related methods |
| * WY 02Jun2015 Bug fix for generic concatenate methods |
| * WY 06Apr2015 Added reverse(byte[]) to reverse byte array elements |
| * WY 06Jan2015 Added reverse() to reverse array elements |
| * WY 10Dec2014 Moved reverseBits() from IMGUtils to here along with BIT_REVERSE_TABLE |
| * WY 08Dec2014 Fixed bug for flipEndian() with more than 32 bit sample data |
| * WY 07Dec2014 Changed method names for byte array to other array types conversion |
| * WY 07Dec2014 Added new methods to work with floating point TIFF images |
| * WY 03Dec2014 Added byteArrayToFloatArray() and byteArrayToDoubleArray() |
| * WY 25Nov2014 Added removeDuplicates() to sort and remove duplicates from int arrays |
| * WY 12Nov2014 Changed the argument sequence for flipEndian() |
| * WY 11Nov2014 Changed flipEndian() to include scan line stride to skip bits |
| * WY 11Nov2014 Added toNBits() to convert byte array to nBits data unit |
| * WY 28Oct2014 Added flipEndian() to work with TIFTweaker mergeTiffImagesEx() |
| */ |
| |
| package pixy.util; |
| |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.lang.reflect.Array; |
| import java.nio.ByteOrder; |
| import java.util.AbstractList; |
| import java.nio.ByteBuffer; |
| import java.nio.DoubleBuffer; |
| import java.nio.FloatBuffer; |
| import java.nio.IntBuffer; |
| import java.nio.LongBuffer; |
| import java.nio.ShortBuffer; |
| |
| /** |
| * Array utility class |
| * |
| * @author Wen Yu, yuwen_66@yahoo.com |
| * @version 1.0 09/18/2012 |
| */ |
| public class ArrayUtils |
| { |
| // Bit mask 0 - 32 bits (inclusive) |
| private static final int[] MASK = { 0x000, |
| 0x1, 0x3, 0x7, 0xf, |
| 0x1f, 0x3f, 0x7f, 0xff, |
| 0x1ff, 0x3ff, 0x7ff, 0xfff, |
| 0x1fff, 0x3fff, 0x7fff, 0xffff, |
| 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, |
| 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, |
| 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, |
| 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff // 32 bits |
| }; |
| |
| // Bit reverse table to work with TIFF FillOrder field. |
| private static final byte[] BIT_REVERSE_TABLE = { |
| (byte)0x00, (byte)0x80, (byte)0x40, (byte)0xc0, (byte)0x20, (byte)0xa0, (byte)0x60, (byte)0xe0, |
| (byte)0x10, (byte)0x90, (byte)0x50, (byte)0xd0, (byte)0x30, (byte)0xb0, (byte)0x70, (byte)0xf0, |
| (byte)0x08, (byte)0x88, (byte)0x48, (byte)0xc8, (byte)0x28, (byte)0xa8, (byte)0x68, (byte)0xe8, |
| (byte)0x18, (byte)0x98, (byte)0x58, (byte)0xd8, (byte)0x38, (byte)0xb8, (byte)0x78, (byte)0xf8, |
| (byte)0x04, (byte)0x84, (byte)0x44, (byte)0xc4, (byte)0x24, (byte)0xa4, (byte)0x64, (byte)0xe4, |
| (byte)0x14, (byte)0x94, (byte)0x54, (byte)0xd4, (byte)0x34, (byte)0xb4, (byte)0x74, (byte)0xf4, |
| (byte)0x0c, (byte)0x8c, (byte)0x4c, (byte)0xcc, (byte)0x2c, (byte)0xac, (byte)0x6c, (byte)0xec, |
| (byte)0x1c, (byte)0x9c, (byte)0x5c, (byte)0xdc, (byte)0x3c, (byte)0xbc, (byte)0x7c, (byte)0xfc, |
| (byte)0x02, (byte)0x82, (byte)0x42, (byte)0xc2, (byte)0x22, (byte)0xa2, (byte)0x62, (byte)0xe2, |
| (byte)0x12, (byte)0x92, (byte)0x52, (byte)0xd2, (byte)0x32, (byte)0xb2, (byte)0x72, (byte)0xf2, |
| (byte)0x0a, (byte)0x8a, (byte)0x4a, (byte)0xca, (byte)0x2a, (byte)0xaa, (byte)0x6a, (byte)0xea, |
| (byte)0x1a, (byte)0x9a, (byte)0x5a, (byte)0xda, (byte)0x3a, (byte)0xba, (byte)0x7a, (byte)0xfa, |
| (byte)0x06, (byte)0x86, (byte)0x46, (byte)0xc6, (byte)0x26, (byte)0xa6, (byte)0x66, (byte)0xe6, |
| (byte)0x16, (byte)0x96, (byte)0x56, (byte)0xd6, (byte)0x36, (byte)0xb6, (byte)0x76, (byte)0xf6, |
| (byte)0x0e, (byte)0x8e, (byte)0x4e, (byte)0xce, (byte)0x2e, (byte)0xae, (byte)0x6e, (byte)0xee, |
| (byte)0x1e, (byte)0x9e, (byte)0x5e, (byte)0xde, (byte)0x3e, (byte)0xbe, (byte)0x7e, (byte)0xfe, |
| (byte)0x01, (byte)0x81, (byte)0x41, (byte)0xc1, (byte)0x21, (byte)0xa1, (byte)0x61, (byte)0xe1, |
| (byte)0x11, (byte)0x91, (byte)0x51, (byte)0xd1, (byte)0x31, (byte)0xb1, (byte)0x71, (byte)0xf1, |
| (byte)0x09, (byte)0x89, (byte)0x49, (byte)0xc9, (byte)0x29, (byte)0xa9, (byte)0x69, (byte)0xe9, |
| (byte)0x19, (byte)0x99, (byte)0x59, (byte)0xd9, (byte)0x39, (byte)0xb9, (byte)0x79, (byte)0xf9, |
| (byte)0x05, (byte)0x85, (byte)0x45, (byte)0xc5, (byte)0x25, (byte)0xa5, (byte)0x65, (byte)0xe5, |
| (byte)0x15, (byte)0x95, (byte)0x55, (byte)0xd5, (byte)0x35, (byte)0xb5, (byte)0x75, (byte)0xf5, |
| (byte)0x0d, (byte)0x8d, (byte)0x4d, (byte)0xcd, (byte)0x2d, (byte)0xad, (byte)0x6d, (byte)0xed, |
| (byte)0x1d, (byte)0x9d, (byte)0x5d, (byte)0xdd, (byte)0x3d, (byte)0xbd, (byte)0x7d, (byte)0xfd, |
| (byte)0x03, (byte)0x83, (byte)0x43, (byte)0xc3, (byte)0x23, (byte)0xa3, (byte)0x63, (byte)0xe3, |
| (byte)0x13, (byte)0x93, (byte)0x53, (byte)0xd3, (byte)0x33, (byte)0xb3, (byte)0x73, (byte)0xf3, |
| (byte)0x0b, (byte)0x8b, (byte)0x4b, (byte)0xcb, (byte)0x2b, (byte)0xab, (byte)0x6b, (byte)0xeb, |
| (byte)0x1b, (byte)0x9b, (byte)0x5b, (byte)0xdb, (byte)0x3b, (byte)0xbb, (byte)0x7b, (byte)0xfb, |
| (byte)0x07, (byte)0x87, (byte)0x47, (byte)0xc7, (byte)0x27, (byte)0xa7, (byte)0x67, (byte)0xe7, |
| (byte)0x17, (byte)0x97, (byte)0x57, (byte)0xd7, (byte)0x37, (byte)0xb7, (byte)0x77, (byte)0xf7, |
| (byte)0x0f, (byte)0x8f, (byte)0x4f, (byte)0xcf, (byte)0x2f, (byte)0xaf, (byte)0x6f, (byte)0xef, |
| (byte)0x1f, (byte)0x9f, (byte)0x5f, (byte)0xdf, (byte)0x3f, (byte)0xbf, (byte)0x7f, (byte)0xff |
| }; |
| |
| // From Effective Java 2nd Edition. |
| public static List<Integer> asList(final int[] a) |
| { |
| if (a == null) |
| throw new NullPointerException(); |
| return new AbstractList<Integer>() {// Concrete implementation built atop skeletal implementation |
| public Integer get(int i) { |
| return a[i]; |
| } |
| |
| @Override public Integer set(int i, Integer val) { |
| int oldVal = a[i]; |
| a[i] = val; |
| return oldVal; |
| } |
| public int size() { |
| return a.length; |
| } |
| }; |
| } |
| |
| public static void bubbleSort(int[] array) { |
| int n = array.length; |
| boolean doMore = true; |
| |
| while (doMore) { |
| n--; |
| doMore = false; // assume this is our last pass over the array |
| |
| for (int i=0; i < n; i++) { |
| if (array[i] > array[i+1]) { |
| // exchange elements |
| int temp = array[i]; |
| array[i] = array[i+1]; |
| array[i+1] = temp; |
| doMore = true; // after an exchange, must look again |
| } |
| } |
| } |
| } |
| |
| public static <T extends Comparable<? super T>> void bubbleSort(T[] array) { |
| int n = array.length; |
| boolean doMore = true; |
| |
| while (doMore) { |
| n--; |
| doMore = false; // assume this is our last pass over the array |
| |
| for (int i = 0; i < n; i++) { |
| if (array[i].compareTo(array[i+1]) > 0) { |
| // exchange elements |
| T temp = array[i]; |
| array[i] = array[i+1]; |
| array[i+1] = temp; |
| doMore = true; // after an exchange, must look again |
| } |
| } |
| } |
| } |
| |
| /** |
| * Since Set doesn't allow duplicates add() return false |
| * if we try to add duplicates into Set and this property |
| * can be used to check if array contains duplicates. |
| * |
| * @param input input array |
| * @return true if input array contains duplicates, otherwise false. |
| */ |
| public static <T> boolean checkDuplicate(T[] input) { |
| Set<T> tempSet = new HashSet<T>(); |
| |
| for (T str : input) { |
| if (!tempSet.add(str)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| public static byte[] concat(byte[] first, byte[]... rest) { |
| if(first == null) { |
| throw new IllegalArgumentException("Firt element is null"); |
| } |
| if(rest.length == 0) return first; |
| // Now the real stuff |
| int totalLength = first.length; |
| |
| for (byte[] array : rest) { |
| totalLength += array.length; |
| } |
| |
| byte[] result = new byte[totalLength]; |
| |
| int offset = first.length; |
| |
| System.arraycopy(first, 0, result, 0, offset); |
| |
| for (byte[] array : rest) { |
| System.arraycopy(array, 0, result, offset, array.length); |
| offset += array.length; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Type safe concatenation of arrays with upper type bound T |
| * <p> |
| * Note: if type parameter is not explicitly supplied, it will be inferred as the |
| * upper bound for the two parameters. |
| * |
| * @param arrays the arrays to be concatenated |
| * @return a concatenation of the input arrays |
| * @throws NullPointerException if any of the input array is null |
| */ |
| public static <T> T[] concat(T[]... arrays) { |
| if(arrays.length == 0) |
| throw new IllegalArgumentException("Varargs length is zero"); |
| |
| if(arrays.length == 1) return arrays[0]; |
| |
| // Now the real stuff |
| int totalLength = 0; |
| // Taking advantage of the compiler type inference |
| Class<?> returnType = arrays.getClass().getComponentType().getComponentType(); |
| |
| for (T[] array : arrays) |
| totalLength += array.length; |
| |
| @SuppressWarnings("unchecked") |
| T[] result = (T[]) Array.newInstance(returnType, totalLength); |
| |
| int offset = 0; |
| for (T[] array : arrays) { |
| System.arraycopy(array, 0, result, offset, array.length); |
| offset += array.length; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Type safe concatenation of arrays with upper type bound T |
| * |
| * @param type type bound for the concatenated array |
| * @param arrays arrays to be concatenated |
| * @return a concatenation of the arrays. |
| * @throws NullPointerException if any of the arrays to be |
| * concatenated is null. |
| */ |
| public static <T> T[] concat(Class<T> type, T[]... arrays) { |
| if(type == null) |
| throw new IllegalArgumentException("Input type class is null"); |
| |
| if(arrays.length == 0) { // Return a zero length array instead of null |
| @SuppressWarnings("unchecked") |
| T[] result = (T[]) Array.newInstance(type, 0); |
| |
| return result; |
| } |
| |
| // Make sure we have at least two arrays to concatenate |
| if(arrays.length == 1) return arrays[0]; |
| |
| int totalLength = 0; |
| for (T[] array : arrays) |
| totalLength += array.length; |
| |
| @SuppressWarnings("unchecked") |
| T[] result = (T[]) Array.newInstance(type, totalLength); |
| |
| int offset = 0; |
| for (T[] array : arrays) { |
| System.arraycopy(array, 0, result, offset, array.length); |
| offset += array.length; |
| } |
| |
| return result; |
| } |
| |
| public static int findEqualOrLess(int[] a, int key) { |
| return findEqualOrLess(a, 0, a.length, key); |
| } |
| |
| /** |
| * Find the index of the element which is equal or less than the key. |
| * The array must be sorted in ascending order. |
| * |
| * @param a the array to be searched |
| * @param key the value to be searched for |
| * @return index of the search key or index of the element which is closest to but less than the search key or |
| * -1 is the search key is less than the first element of the array. |
| */ |
| |
| public static int findEqualOrLess(int[] a, int fromIndex, int toIndex, int key) { |
| int index = Arrays.binarySearch(a, fromIndex, toIndex, key); |
| |
| // -index - 1 is the insertion point if not found, so -index - 1 -1 is the less position |
| if(index < 0) { |
| index = -index - 1 - 1; |
| } |
| |
| // The index of the element which is either equal or less than the key |
| return index; |
| } |
| |
| public static <T> int findEqualOrLess(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) { |
| int index = Arrays.binarySearch(a, fromIndex, toIndex, key, c); |
| |
| // -index - 1 is the insertion point if not found |
| if(index < 0) { |
| index = -index - 1 - 1; |
| } |
| |
| return index; |
| } |
| |
| public static <T> int findEqualOrLess(T[] a, T key, Comparator<? super T> c) { |
| return findEqualOrLess(a, 0, a.length, key, c); |
| } |
| |
| /** |
| * Flip the endian of the input byte-compacted array |
| * |
| * @param input the input byte array which is byte compacted |
| * @param offset the offset to start the reading input |
| * @param len number of bytes to read |
| * @param bits number of bits for the data before compaction |
| * @param scanLineStride scan line stride to skip bits |
| * @param bigEndian whether or not the input data is in big endian order |
| * |
| * @return a byte array with the endian flipped |
| */ |
| public static byte[] flipEndian(byte[] input, int offset, int len, int bits, int scanLineStride, boolean bigEndian) { |
| long value = 0; |
| int bits_remain = 0; |
| long temp_byte = 0; // Must make this long, otherwise will give wrong result for bits > 32 |
| int empty_bits = 8; |
| |
| byte[] output = new byte[input.length]; |
| |
| int strideCounter = 0; |
| |
| int end = offset + len; |
| int bufIndex = 0; |
| |
| int temp = bits; |
| boolean bigEndianOut = !bigEndian; |
| |
| loop: |
| while(true) { |
| |
| if(!bigEndian) |
| value = (temp_byte >> (8-bits_remain)); |
| else |
| value = (temp_byte & MASK[bits_remain]); |
| |
| while (bits > bits_remain) |
| { |
| if(offset >= end) { |
| break loop; |
| } |
| |
| temp_byte = input[offset++]&0xff; |
| |
| if(bigEndian) |
| value = ((value<<8)|temp_byte); |
| else |
| value |= (temp_byte<<bits_remain); |
| |
| bits_remain += 8; |
| } |
| |
| bits_remain -= bits; |
| |
| if(bigEndian) |
| value = (value>>bits_remain); |
| |
| // Write bits bit length value in opposite endian |
| if(bigEndianOut) { |
| temp = bits-empty_bits; |
| output[bufIndex] |= ((value>>temp)&MASK[empty_bits]); |
| |
| while(temp > 8) |
| { |
| output[++bufIndex] |= ((value>>(temp-8))&MASK[8]); |
| temp -= 8; |
| } |
| |
| if(temp > 0) { |
| output[++bufIndex] |= ((value&MASK[temp])<<(8-temp)); |
| temp -= 8; |
| } |
| } else { // Little endian |
| temp = bits; |
| output[bufIndex] |= ((value&MASK[empty_bits])<<(8-empty_bits)); |
| value >>= empty_bits; |
| temp -= empty_bits; |
| // If the code is longer than the empty_bits |
| while(temp > 8) { |
| output[++bufIndex] |= (value&0xff); |
| value >>= 8; |
| temp -= 8; |
| } |
| |
| if(temp > 0) |
| { |
| output[++bufIndex] |= (value&MASK[temp]); |
| temp -= 8; |
| } |
| } |
| |
| empty_bits = -temp; |
| |
| if(++strideCounter%scanLineStride == 0) { |
| empty_bits = 0; |
| bits_remain = 0; |
| } |
| } |
| |
| return output; |
| } |
| |
| // From http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java |
| // returns all higher 16 bits as 0 for all results |
| public static int fromFloat(float fval) |
| { |
| int fbits = Float.floatToIntBits(fval); |
| int sign = fbits >>> 16 & 0x8000; // sign only |
| int val = (fbits & 0x7fffffff) + 0x1000; // rounded value |
| |
| if(val >= 0x47800000) // might be or become NaN/Inf |
| { // avoid Inf due to rounding |
| if( (fbits & 0x7fffffff) >= 0x47800000) |
| { // is or must become NaN/Inf |
| if(val < 0x7f800000) // was value but too large |
| return sign | 0x7c00; // make it +/-Inf |
| return sign | 0x7c00 | // remains +/-Inf or NaN |
| (fbits & 0x007fffff) >>> 13; // keep NaN (and Inf) bits |
| } |
| return sign | 0x7bff; // unrounded not quite Inf |
| } |
| if(val >= 0x38800000) // remains normalized value |
| return sign | val - 0x38000000 >>> 13; // exp - 127 + 15 |
| if(val < 0x33000000) // too small for subnormal |
| return sign; // becomes +/-0 |
| val = (fbits & 0x7fffffff) >>> 23; // tmp exp for subnormal calc |
| return sign | ((fbits & 0x7fffff | 0x800000) // add subnormal bit |
| + (0x800000 >>> val - 102) // round depending on cut off |
| >>> 126 - val); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0 |
| } |
| |
| /** |
| * Since nonzero-length array is always mutable, we should return |
| * a clone of the underlying array as BIT_REVERSE_TABLE.clone(). |
| * |
| * @return the byte reverse table. |
| */ |
| public static byte[] getBitReverseTable() { |
| return BIT_REVERSE_TABLE.clone(); |
| } |
| |
| // Insertion sort |
| public static void insertionsort(int[] array) { |
| insertionsort(array, 0, array.length - 1); |
| } |
| |
| public static void insertionsort(int[] array, int start, int end) { |
| int j; |
| |
| for (int i = start + 1; i < end + 1; i++) |
| { |
| |
| int temp = array[i]; |
| for ( j = i; j > start && temp <= array[j-1]; j-- ) |
| array[j] = array[j-1]; |
| // Move temp to the right place |
| array[j] = temp; |
| } |
| } |
| |
| // Insertion sort |
| public static <T extends Comparable<? super T>> void insertionsort(T[] array) { |
| insertionsort(array, 0, array.length - 1); |
| } |
| |
| // Insertion sort |
| public static <T extends Comparable<? super T>> void insertionsort(T[] array, int start, int end) { |
| int j; |
| |
| for (int i = start + 1; i < end + 1; i++) |
| { |
| T temp = array[i]; |
| for ( j = i; j > start && temp.compareTo(array[j-1]) <= 0; j-- ) |
| array[j] = array[j-1]; |
| // Move temp to the right place |
| array[j] = temp; |
| } |
| } |
| |
| // Merge sort |
| public static void mergesort(int[] array) { |
| mergesort(array, new int[array.length], 0, array.length - 1); |
| } |
| |
| public static void mergesort(int[] array, int left, int right) { |
| if(left < 0 || right > array.length - 1) throw new IllegalArgumentException("Array index out of bounds"); |
| mergesort(array, new int[array.length], left, right); |
| } |
| |
| private static void mergesort(int[] array, int[] temp, int left, int right) { |
| // check the base case |
| if (left < right) { |
| // Get the index of the element which is in the middle |
| int middle = left + (right - left) / 2; |
| // Sort the left side of the array |
| mergesort(array, temp, left, middle); |
| // Sort the right side of the array |
| mergesort(array, temp, middle + 1, right); |
| // Merge the left and the right |
| merge(array, temp, left, middle, right); |
| } |
| } |
| |
| public static <T extends Comparable<? super T>> void mergesort(T[] array) { |
| mergesort(array, 0, array.length - 1); |
| } |
| |
| public static <T extends Comparable<? super T>> void mergesort(T[] array, int left, int right) { |
| if(left < 0 || right > array.length - 1) throw new IllegalArgumentException("Array index out of bounds"); |
| @SuppressWarnings("unchecked") |
| T[] temp = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length); |
| mergesort(array, temp, left, right); |
| } |
| |
| // Merge sort |
| private static <T extends Comparable<? super T>> void mergesort(T[] array, T[] temp, int left, int right) { |
| // check the base case |
| if (left < right) { |
| // Get the index of the element which is in the middle |
| int middle = left + (right - left) / 2; |
| // Sort the left side of the array |
| mergesort(array, temp, left, middle); |
| // Sort the right side of the array |
| mergesort(array, temp, middle + 1, right); |
| // Merge the left and the right |
| merge(array, temp, left, middle, right); |
| } |
| } |
| |
| private static <T extends Comparable<? super T>> void merge(T[] array, T[] temp, int left, int middle, int right) { |
| // Copy both parts into the temporary array |
| for (int i = left; i <= right; i++) { |
| temp[i] = array[i]; |
| } |
| int i = left; |
| int j = middle + 1; |
| int k = left; |
| while (i <= middle && j <= right) { |
| if (temp[i].compareTo(temp[j]) <= 0) { |
| array[k] = temp[i]; |
| i++; |
| } else { |
| array[k] = temp[j]; |
| j++; |
| } |
| k++; |
| } |
| while (i <= middle) { |
| array[k] = temp[i]; |
| k++; |
| i++; |
| } |
| } |
| |
| private static void merge(int[] array, int[] temp, int left, int middle, int right) { |
| // Copy both parts into the temporary array |
| for (int i = left; i <= right; i++) { |
| temp[i] = array[i]; |
| } |
| int i = left; |
| int j = middle + 1; |
| int k = left; |
| while (i <= middle && j <= right) { |
| if (temp[i] <= temp[j]) { |
| array[k] = temp[i]; |
| i++; |
| } else { |
| array[k] = temp[j]; |
| j++; |
| } |
| k++; |
| } |
| while (i <= middle) { |
| array[k] = temp[i]; |
| k++; |
| i++; |
| } |
| } |
| |
| /** |
| * Packs all or part of the input byte array which uses "bits" bits to use all 8 bits. |
| * |
| * @param input input byte array |
| * @param start offset of the input array to start packing |
| * @param bits number of bits used by the input array |
| * @param len number of bytes from the input to be packed |
| * @return the packed byte array |
| */ |
| public static byte[] packByteArray(byte[] input, int start, int bits, int len) { |
| // |
| if(bits == 8) return ArrayUtils.subArray(input, start, len); |
| if(bits > 8 || bits <= 0) throw new IllegalArgumentException("Invalid value of bits: " + bits); |
| |
| byte[] packedBytes = new byte[(bits*len + 7)>>3]; |
| short mask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; |
| |
| int index = 0; |
| int empty_bits = 8; |
| int end = start + len; |
| |
| for(int i = start; i < end; i++) { |
| // If we have enough space for input byte, one step operation |
| if(empty_bits >= bits) { |
| packedBytes[index] |= ((input[i]&mask[bits])<<(empty_bits-bits)); |
| empty_bits -= bits; |
| if(empty_bits == 0) { |
| index++; |
| empty_bits = 8; |
| } |
| } else { // Otherwise two step operation |
| packedBytes[index++] |= ((input[i]>>(bits-empty_bits))&mask[empty_bits]); |
| packedBytes[index] |= ((input[i]&mask[bits-empty_bits])<<(8-bits+empty_bits)); |
| empty_bits += (8-bits); |
| } |
| } |
| |
| return packedBytes; |
| } |
| |
| /** |
| * Packs all or part of the input byte array which uses "bits" bits to use all 8 bits. |
| * <p> |
| * We assume len is a multiplication of stride. The parameter stride controls the packing |
| * unit length and different units <b>DO NOT</b> share same byte. This happens when packing |
| * image data where each scan line <b>MUST</b> start at byte boundary like TIFF. |
| * |
| * @param input input byte array to be packed |
| * @param stride length of packing unit |
| * @param start offset of the input array to start packing |
| * @param bits number of bits used in each byte of the input |
| * @param len number of input bytes to be packed |
| * @return the packed byte array |
| */ |
| public static byte[] packByteArray(byte[] input, int stride, int start, int bits, int len) { |
| // |
| if(bits == 8) return ArrayUtils.subArray(input, start, len); |
| if(bits > 8 || bits <= 0) throw new IllegalArgumentException("Invalid value of bits: " + bits); |
| |
| int bitsPerStride = bits*stride; |
| int numOfStrides = len/stride; |
| byte[] packedBytes = new byte[((bitsPerStride + 7)>>3)*numOfStrides]; |
| short mask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; |
| |
| int index = 0; |
| int empty_bits = 8; |
| int end = start + len; |
| int strideCounter = 0; |
| |
| for(int i = start; i < end; i++) { |
| // If we have enough space for input byte, one step operation |
| if(empty_bits >= bits) { |
| packedBytes[index] |= ((input[i]&mask[bits])<<(empty_bits-bits)); |
| empty_bits -= bits; |
| } else { // Otherwise, split the pixel between two bytes. |
| //This will never happen for 1, 2, 4, 8 bits color depth image |
| packedBytes[index++] |= ((input[i]>>(bits-empty_bits))&mask[empty_bits]); |
| packedBytes[index] |= ((input[i]&mask[bits-empty_bits])<<(8-bits+empty_bits)); |
| empty_bits += (8-bits); |
| } |
| // Check to see if we need to move to next byte |
| if(++strideCounter%stride == 0 || empty_bits == 0) { |
| index++; |
| empty_bits = 8; |
| } |
| } |
| |
| return packedBytes; |
| } |
| |
| // Quick sort |
| public static void quicksort(int[] array) { |
| quicksort (array, 0, array.length - 1); |
| } |
| |
| public static void quicksort (int[] array, int start, int end) { |
| int inner = start; |
| int outer = end; |
| int mid = (start + end) / 2; |
| |
| do { |
| // work in from the start until we find a swap to the |
| // other partition is needed |
| while ((inner < mid) && (array[inner] <= array[mid])) |
| inner++; |
| |
| // work in from the end until we find a swap to the |
| // other partition is needed |
| |
| while ((outer > mid) && (array[outer] >= array[mid])) |
| outer--; |
| |
| // if inner index <= outer index, swap elements |
| if (inner < mid && outer > mid) { |
| swap(array, inner, outer); |
| inner++; |
| outer--; |
| } else if (inner < mid) { |
| swap(array, inner, mid - 1); |
| swap(array, mid, mid - 1); |
| mid--; |
| } else if (outer >mid) { |
| swap(array, outer, mid + 1); |
| swap(array, mid, mid + 1); |
| mid++; |
| } |
| } while (inner !=outer); |
| |
| // recursion |
| if ((mid - 1) > start) quicksort(array, start, mid - 1); |
| if (end > (mid + 1)) quicksort(array, mid + 1, end); |
| } |
| |
| // Quick sort |
| public static <T extends Comparable<? super T>> void quicksort (T[] array) { |
| quicksort(array, 0, array.length - 1); |
| } |
| |
| // Quick sort |
| public static <T extends Comparable<? super T>> void quicksort (T[] array, int low, int high) { |
| int i = low, j = high; |
| // Get the pivot element from the middle of the list |
| T pivot = array[low + (high-low)/2]; |
| |
| // Divide into two lists |
| while (i <= j) { |
| // If the current value from the left list is smaller then the pivot |
| // element then get the next element from the left list |
| while (array[i].compareTo(pivot) < 0) { |
| i++; |
| } |
| // If the current value from the right list is larger then the pivot |
| // element then get the next element from the right list |
| while (array[j].compareTo(pivot) > 0) { |
| j--; |
| } |
| |
| // If we have found a values in the left list which is larger then |
| // the pivot element and if we have found a value in the right list |
| // which is smaller then the pivot element then we exchange the |
| // values. |
| // As we are done we can increase i and j |
| if (i <= j) { |
| swap(array, i, j); |
| i++; |
| j--; |
| } |
| } |
| // Recursion |
| if (low < j) |
| quicksort(array, low, j); |
| if (i < high) |
| quicksort(array, i, high); |
| } |
| |
| // Based on java2novice.com example |
| /** |
| * Remove duplicate elements from an int array |
| * |
| * @param input input unsorted int array |
| * @return a sorted int array with unique elements |
| */ |
| public static int[] removeDuplicates(int[] input) { |
| //return if the array length is less than 2 |
| if(input.length < 2){ |
| return input; |
| } |
| |
| // Sort the array first |
| Arrays.sort(input); |
| |
| int j = 0; |
| int i = 1; |
| |
| while(i < input.length){ |
| if(input[i] == input[j]){ |
| i++; |
| } else{ |
| input[++j] = input[i++]; |
| } |
| } |
| |
| int[] output = new int[j + 1]; |
| |
| System.arraycopy(input, 0, output, 0, j + 1); |
| |
| return output; |
| } |
| |
| // Reverse the bit order (bit sex) of a byte array |
| public static void reverseBits(byte[] input) { |
| for(int i = input.length - 1; i >= 0; i--) |
| input[i] = BIT_REVERSE_TABLE[input[i]&0xff]; |
| } |
| |
| public static byte[] reverse(byte[] array) { |
| if (array == null) |
| throw new IllegalArgumentException("Input array is null"); |
| int left = 0; |
| int right = array.length - 1; |
| byte tmp; |
| while (left < right) { |
| tmp = array[right]; |
| array[right] = array[left]; |
| array[left] = tmp; |
| left++; |
| right--; |
| } |
| |
| return array; |
| } |
| |
| // Reverse the array |
| public static <T> void reverse(T[] data) { |
| for (int left = 0, right = data.length - 1; left < right; left++, right--) { |
| T temp = data[left]; |
| data[left] = data[right]; |
| data[right] = temp; |
| } |
| } |
| |
| // Shell sort |
| public static void shellsort(int[] array) { |
| shellsort(array, 0, array.length - 1); |
| } |
| |
| public static void shellsort(int[] array, int start, int end) { |
| if(start < 0 || end < 0 || start > end || end > array.length -1) throw new IllegalArgumentException("Array index out of bounds"); |
| int gap = 1; |
| int len = end - start + 1; |
| // Generate Knuth sequence 1, 4, 13, 40, 121, 364,1093, 3280, 9841 ... |
| while(gap < len) gap = 3*gap + 1; |
| while ( gap > 0 ) |
| { |
| int begin = start + gap; |
| for (int i = begin; i <= end; i++) |
| { |
| int temp = array[i]; |
| int j = i; |
| while ( j >= begin && temp <= array[j - gap]) |
| { |
| array[j] = array[j - gap]; |
| j -= gap; |
| } |
| array[j] = temp; |
| } |
| gap /= 3; |
| } |
| } |
| |
| // Shell sort |
| public static <T extends Comparable<? super T>> void shellsort(T[] array) { |
| shellsort(array, 0, array.length - 1); |
| } |
| |
| // Shell sort |
| public static <T extends Comparable<? super T>> void shellsort(T[] array, int start, int end) { |
| if(start < 0 || end < 0 || start > end || end > array.length - 1) throw new IllegalArgumentException("Array index out of bounds"); |
| int gap = 1; |
| int len = end - start + 1; |
| // Generate Knuth sequence 1, 4, 13, 40, 121, 364,1093, 3280, 9841 ... |
| while(gap < len) gap = 3*gap + 1; |
| while ( gap > 0 ) |
| { |
| int begin = start + gap; |
| for (int i = begin; i <= end; i++) |
| { |
| T temp = array[i]; |
| int j = i; |
| while ( j >= begin && temp.compareTo(array[j - gap]) <= 0) |
| { |
| array[j] = array[j - gap]; |
| j -= gap; |
| } |
| array[j] = temp; |
| } |
| gap /= 3; |
| } |
| } |
| |
| public static byte[] subArray(byte[] src, int offset, int len) { |
| if(offset == 0 && len == src.length) return src; |
| if((offset < 0 || offset >= src.length) || (offset + len > src.length)) |
| throw new IllegalArgumentException("Copy range out of array bounds"); |
| byte[] dest = new byte[len]; |
| System.arraycopy(src, offset, dest, 0, len); |
| |
| return dest; |
| } |
| |
| private static final void swap(int[] array, int a, int b) { |
| int temp = array[a]; |
| array[a] = array[b]; |
| array[b] = temp; |
| } |
| |
| private static final <T> void swap(T[] array, int a, int b) { |
| T temp = array[a]; |
| array[a] = array[b]; |
| array[b] = temp; |
| } |
| |
| public static float[] to16BitFloatArray(byte[] data, boolean bigEndian) { |
| short[] shorts = (short[])toNBits(16, data, Integer.MAX_VALUE, bigEndian); |
| float[] floats = new float[shorts.length]; |
| |
| for(int i = 0; i < floats.length; i++) { |
| floats[i] = toFloat(shorts[i]); |
| } |
| |
| return floats; |
| } |
| |
| /* |
| * Tries to convert 24 bit floating point sample to 32 bit float data. |
| * Up to now, there has been no way to do it correctly and there might be no |
| * correct way to do this because 24 bit is not an IEEE floating point type. |
| * 24 bit floating point images appear too dark using this conversion. |
| */ |
| public static float[] to24BitFloatArray(byte[] data, boolean bigEndian) { |
| int[] ints = (int[])toNBits(24, data, Integer.MAX_VALUE, bigEndian); |
| float[] floats = new float[ints.length]; |
| |
| for(int i = 0; i < floats.length; i++) { |
| /** |
| int bits = ints[i]<<8; |
| int sign = ((bits & 0x80000000) == 0) ? 1 : -1; |
| int exponent = ((bits & 0x7f800000) >> 23); |
| int mantissa = (bits & 0x007fffff); |
| |
| mantissa |= 0x00800000; |
| |
| floats[i] = (float)(sign * mantissa * Math.pow(2, exponent-150)); |
| */ |
| floats[i] = Float.intBitsToFloat(ints[i]<<8); |
| } |
| |
| return floats; |
| } |
| |
| // Convert byte array to long array, then to integer array discarding the higher bits |
| public static int[] to32BitsLongArray(byte[] data, boolean bigEndian) { |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| LongBuffer longBuf = byteBuffer.asLongBuffer(); |
| long[] array = new long[longBuf.remaining()]; |
| longBuf.get(array); |
| |
| int[] iArray = new int[array.length]; |
| |
| int i = 0; |
| |
| for(long l : array) { |
| iArray[i++] = (int)l; |
| } |
| |
| return iArray; |
| } |
| |
| public static byte[] toByteArray(int value) { |
| return new byte[] { |
| (byte)value, |
| (byte)(value >>> 8), |
| (byte)(value >>> 16), |
| (byte)(value >>> 24) |
| }; |
| } |
| |
| public static byte[] toByteArray(int[] data, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| IntBuffer intBuffer = byteBuffer.asIntBuffer(); |
| intBuffer.put(data); |
| |
| byte[] array = byteBuffer.array(); |
| |
| return array; |
| } |
| |
| public static byte[] toByteArray(long[] data, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 8); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| LongBuffer longBuffer = byteBuffer.asLongBuffer(); |
| longBuffer.put(data); |
| |
| byte[] array = byteBuffer.array(); |
| |
| return array; |
| } |
| |
| public static byte[] toByteArray(short value) { |
| return new byte[] { |
| (byte)value, (byte)(value >>> 8)}; |
| } |
| |
| public static byte[] toByteArray(short[] data, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 2); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| ShortBuffer shortBuffer = byteBuffer.asShortBuffer(); |
| shortBuffer.put(data); |
| |
| byte[] array = byteBuffer.array(); |
| |
| return array; |
| } |
| |
| public static byte[] toByteArrayMM(int value) { |
| return new byte[] { |
| (byte)(value >>> 24), |
| (byte)(value >>> 16), |
| (byte)(value >>> 8), |
| (byte)value}; |
| } |
| |
| public static byte[] toByteArrayMM(short value) { |
| return new byte[] { |
| (byte)(value >>> 8), (byte)value}; |
| } |
| |
| public static double[] toDoubleArray(byte[] data, boolean bigEndian) { |
| return toDoubleArray(data, 0, data.length, bigEndian); |
| } |
| |
| public static double[] toDoubleArray(byte[] data, int offset, int len, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| DoubleBuffer doubleBuf = byteBuffer.asDoubleBuffer(); |
| double[] array = new double[doubleBuf.remaining()]; |
| doubleBuf.get(array); |
| |
| return array; |
| } |
| |
| // From http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java |
| // Converts integer to float ignores the higher 16 bits |
| public static float toFloat(int lbits) { |
| // ignores the higher 16 bits |
| int mant = lbits & 0x03ff; // 10 bits mantissa |
| int exp = lbits & 0x7c00; // 5 bits exponent |
| |
| if(exp == 0x7c00 ) { // NaN/Inf |
| exp = 0x3fc00; // -> NaN/Inf |
| } else if(exp != 0) { // normalized value |
| exp += 0x1c000; // exp - 15 + 127 |
| if( mant == 0 && exp > 0x1c400) // smooth transition |
| return Float.intBitsToFloat( |
| ( lbits & 0x8000) << 16 |
| | exp << 13 | 0x3ff); |
| } else if(mant != 0) { // && exp==0 -> subnormal |
| exp = 0x1c400; // make it normal |
| do { |
| mant <<= 1; // mantissa * 2 |
| exp -= 0x400; // decrease exp by 1 |
| } while((mant & 0x400) == 0); // while not normal |
| mant &= 0x3ff; // discard subnormal bit |
| } // else +/-0 -> +/-0 |
| |
| return Float.intBitsToFloat( // combine all parts |
| ( lbits & 0x8000 ) << 16 // sign << ( 31 - 15 ) |
| | ( exp | mant ) << 13 ); // value << ( 23 - 10 ) |
| } |
| |
| public static float[] toFloatArray(byte[] data, boolean bigEndian) { |
| return toFloatArray(data, 0, data.length, bigEndian); |
| } |
| |
| public static float[] toFloatArray(byte[] data, int offset, int len, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| FloatBuffer floatBuf = byteBuffer.asFloatBuffer(); |
| float[] array = new float[floatBuf.remaining()]; |
| floatBuf.get(array); |
| |
| return array; |
| } |
| |
| public static int[] toIntArray(byte[] data, boolean bigEndian) { |
| return toIntArray(data, 0, data.length, bigEndian); |
| } |
| |
| public static int[] toIntArray(byte[] data, int offset, int len, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| IntBuffer intBuf = byteBuffer.asIntBuffer(); |
| int[] array = new int[intBuf.remaining()]; |
| intBuf.get(array); |
| |
| return array; |
| } |
| |
| public static long[] toLongArray(byte[] data, boolean bigEndian) { |
| return toLongArray(data, 0, data.length, bigEndian); |
| } |
| |
| public static long[] toLongArray(byte[] data, int offset, int len, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| LongBuffer longBuf = byteBuffer.asLongBuffer(); |
| long[] array = new long[longBuf.remaining()]; |
| longBuf.get(array); |
| |
| return array; |
| } |
| |
| /** |
| * Converts an input byte array to nBits data array using the smallest data type which |
| * can hold the nBits data. Each data type contains only one data element. |
| * |
| * @param nBits number of bits for the data element |
| * @param input the input array for the data elements |
| * @param stride scan line stride used to discard remaining bits |
| * @param bigEndian the packing order of the bits. True if bigEndian, otherwise false. |
| * |
| * @return an array of the smallest data type which can hold the nBits data |
| */ |
| public static Object toNBits(int nBits, byte[] input, int stride, boolean bigEndian) { |
| int value = 0; |
| int bits_remain = 0; |
| int temp_byte = 0; |
| |
| byte[] byteOutput = null; |
| short[] shortOutput = null; |
| int[] intOutput = null; |
| Object output = null; |
| |
| int outLen = (int)((input.length*8L + nBits - 1)/nBits); |
| |
| if(nBits <= 8) { |
| byteOutput = new byte[outLen]; |
| output = byteOutput; |
| } else if(nBits <= 16) { |
| shortOutput = new short[outLen]; |
| output = shortOutput; |
| } else if(nBits <= 32){ |
| intOutput = new int[outLen]; |
| output = intOutput; |
| } else { |
| throw new IllegalArgumentException("nBits exceeds limit - maximum 32"); |
| } |
| |
| int offset = 0; |
| int index = 0; |
| |
| int strideCounter = 0; |
| |
| loop: |
| while(true) { |
| |
| if(!bigEndian) |
| value = (temp_byte >> (8-bits_remain)); |
| else |
| value = (temp_byte & MASK[bits_remain]); |
| |
| while (nBits > bits_remain) |
| { |
| if(offset >= input.length) { |
| break loop; |
| } |
| |
| temp_byte = input[offset++]&0xff; |
| |
| if(bigEndian) |
| value = ((value<<8)|temp_byte); |
| else |
| value |= (temp_byte<<bits_remain); |
| |
| bits_remain += 8; |
| } |
| |
| bits_remain -= nBits; |
| |
| if(bigEndian) |
| value = (value>>(bits_remain)); |
| |
| value &= MASK[nBits]; |
| |
| if(++strideCounter%stride == 0) { |
| bits_remain = 0; // Discard the remaining bits |
| } |
| |
| if(nBits <= 8) byteOutput[index++] = (byte)value; |
| else if(nBits <= 16) shortOutput[index++] = (short)value; |
| else intOutput[index++] = value; |
| } |
| |
| return output; |
| } |
| |
| public static double[] toPrimitive(Double[] doubles) { |
| double[] dArray = new double[doubles.length]; |
| int i = 0; |
| |
| for (double d : doubles) { |
| dArray[i++] = d; |
| } |
| |
| return dArray; |
| } |
| |
| public static float[] toPrimitive(Float[] floats) { |
| float[] fArray = new float[floats.length]; |
| int i = 0; |
| |
| for (float f : floats) { |
| fArray[i++] = f; |
| } |
| |
| return fArray; |
| } |
| |
| public static int[] toPrimitive(Integer[] integers) { |
| int[] ints = new int[integers.length]; |
| int i = 0; |
| |
| for (int n : integers) { |
| ints[i++] = n; |
| } |
| |
| return ints; |
| } |
| |
| public static long[] toPrimitive(Long[] longs) { |
| long[] lArray = new long[longs.length]; |
| int i = 0; |
| |
| for (long l : longs) { |
| lArray[i++] = l; |
| } |
| |
| return lArray; |
| } |
| |
| public static short[] toPrimitive(Short[] shorts) { |
| short[] sArray = new short[shorts.length]; |
| int i = 0; |
| |
| for (short s : shorts) { |
| sArray[i++] = s; |
| } |
| |
| return sArray; |
| } |
| |
| public static short[] toShortArray(byte[] data, boolean bigEndian) { |
| return toShortArray(data, 0, data.length, bigEndian); |
| } |
| |
| public static short[] toShortArray(byte[] data, int offset, int len, boolean bigEndian) { |
| |
| ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len); |
| |
| if (bigEndian) { |
| byteBuffer.order(ByteOrder.BIG_ENDIAN); |
| } else { |
| byteBuffer.order(ByteOrder.LITTLE_ENDIAN); |
| } |
| |
| ShortBuffer shortBuf = byteBuffer.asShortBuffer(); |
| short[] array = new short[shortBuf.remaining()]; |
| shortBuf.get(array); |
| |
| return array; |
| } |
| |
| private ArrayUtils(){} // Prevents instantiation |
| } |