/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2016, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.image.io.plugin;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import org.apache.sis.util.ArgumentChecks;
/**
* A {@link ReadableByteChannel} with a {@code read} method that return all available bytes with theirs bits reversed.<br/>
* This class is used for read Tiff or GeoTiff file with a tag tiff fill order at value 2 which means all read byte have their bits reversed.
*
* @see #read(java.nio.ByteBuffer)
* @author Remi Marechal (Geomatys).
*/
final class ReversedBitsChannel implements ReadableByteChannel {
/**
* Array which contain reversed value.
*/
private static final byte[] REVERSE = new byte[256];
static {
for (int p = 0; p < 256; p++) {
REVERSE[p] = (byte) (Integer.reverse(p) >>> 24);
}
}
/**
* ReadableByteChannel where to read originals Bytes.
*/
private final ReadableByteChannel input;
/**
* Creates a new {@code ReversedBitsChannel} wrapping the given {@code ReadableByteChannel}.
*
* @param input given {@code ReadableByteChannel}.
*/
public ReversedBitsChannel(final ReadableByteChannel input) {
ArgumentChecks.ensureNonNull("input", input);
this.input = input;
}
/**
* Reads a sequence of bytes from this channel into the given buffer.<br/>
* All <code>bits</code> from each read <code>byte</code> are reverse which means <br/>
* lower order bit becomme high order bit and vice - versa.<br/><br/>
*
* For example : <br/>
* read <code>byte</code> : 11010100<br/>
* becomme : 00101011<br/>
* Same operation affected on all read <code>bytes</code> into given buffer.<br/>
* Moreother buffer properties like position, limit and others are same like a normal read use.
*
* @param dst
* The buffer into which bytes are to be transferred
*
* @return The number of bytes read, possibly zero, or <tt>-1</tt> if the
* channel has reached end-of-stream
*
* @throws NonReadableChannelException
* If this channel was not opened for reading
*
* @throws ClosedChannelException
* If this channel is closed
*
* @throws AsynchronousCloseException
* If another thread closes this channel
* while the read operation is in progress
*
* @throws ClosedByInterruptException
* If another thread interrupts the current thread
* while the read operation is in progress, thereby
* closing the channel and setting the current thread's
* interrupt status
*
* @throws IOException
* If some other I/O error occurs
*/
@Override
public int read(ByteBuffer dst) throws IOException {
final int dstPos = dst.position();
final int n = input.read(dst);
if (n > 0) {
reverseBytes(dst, dstPos, dstPos + n);
}
return n;
}
/**
* Reverse <code>bits</code> sens of all byte from buffer between inclusive positionMin and exclusive positionMax.
*
* @param buffer buffer where byte will be reverse.
* @param position first reverse byte position in buffer.
* @param limit last exclusive reverse byte position in buffer.
*/
private static void reverseBytes(final ByteBuffer buffer, int position, final int limit) {
while (position < limit) {
final byte b = buffer.get(position);
buffer.put(position++, REVERSE[b & 0xFF]);
}
}
/**
* {@inheritDoc }
*/
@Override
public boolean isOpen() {
return input.isOpen();
}
/**
* {@inheritDoc }
*/
@Override
public void close() throws IOException {
input.close();
}
}