/*
* #%L
* BSD implementations of Bio-Formats readers and writers
* %%
* Copyright (C) 2005 - 2015 Open Microscopy Environment:
* - Board of Regents of the University of Wisconsin-Madison
* - Glencoe Software, Inc.
* - University of Dundee
* %%
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* #L%
*/
package loci.formats.codec;
import java.io.IOException;
import loci.common.RandomAccessInputStream;
import loci.formats.FormatException;
import loci.formats.UnsupportedCompressionException;
/**
* Implements encoding and decoding methods for Apple RPZA. This code was
* adapted from the RPZA codec for ffmpeg - see http://ffmpeg.mplayerhq.hu
*/
public class RPZACodec extends BaseCodec {
// -- Fields --
private int totalBlocks, pixelPtr, rowPtr, stride;
/* @see Codec#compress(byte[], CodecOptions) */
@Override
public byte[] compress(byte[] input, CodecOptions options)
throws FormatException
{
throw new UnsupportedCompressionException("RPZA compression not supported.");
}
/**
* The CodecOptions parameter should have the following fields set:
* {@link CodecOptions#width width}
* {@link CodecOptions#height height}
*
* @see Codec#decompress(RandomAccessInputStream, CodecOptions)
*/
@Override
public byte[] decompress(RandomAccessInputStream in, CodecOptions options)
throws FormatException, IOException
{
if (in == null)
throw new IllegalArgumentException("No data to decompress.");
if (options == null) options = CodecOptions.getDefaultOptions();
in.skipBytes(8);
int plane = options.width * options.height;
stride = options.width;
int rowInc = stride - 4;
short opcode;
int nBlocks;
int colorA = 0, colorB;
int[] color4 = new int[4];
int index, idx;
int ta, tb;
int blockPtr = 0;
rowPtr = pixelPtr = 0;
int pixelX, pixelY;
int[] pixels = new int[plane];
byte[] rtn = new byte[plane * 3];
while (in.read() != (byte) 0xe1);
in.skipBytes(3);
totalBlocks = ((options.width + 3) / 4) * ((options.height + 3) / 4);
while (in.getFilePointer() + 2 < in.length()) {
opcode = in.readByte();
nBlocks = (opcode & 0x1f) + 1;
if ((opcode & 0x80) == 0) {
if (in.getFilePointer() >= in.length()) break;
colorA = (opcode << 8) | in.read();
opcode = 0;
if (in.getFilePointer() >= in.length()) break;
if ((in.read() & 0x80) != 0) {
opcode = 0x20;
nBlocks = 1;
}
in.seek(in.getFilePointer() - 1);
}
switch (opcode & 0xe0) {
case 0x80:
while (nBlocks-- > 0) {
updateBlock(options.width);
}
break;
case 0xa0:
if (in.getFilePointer() + 2 >= in.length()) break;
colorA = in.readShort();
while (nBlocks-- > 0) {
blockPtr = rowPtr + pixelPtr;
for (pixelY=0; pixelY<4; pixelY++) {
for (pixelX=0; pixelX<4; pixelX++) {
if (blockPtr >= pixels.length) break;
pixels[blockPtr] = colorA;
short s = (short) (pixels[blockPtr] & 0x7fff);
unpack(s, rtn, blockPtr, pixels.length);
blockPtr++;
}
blockPtr += rowInc;
}
updateBlock(options.width);
}
break;
case 0xc0:
case 0x20:
if (in.getFilePointer() + 2 >= in.length()) break;
if ((opcode & 0xe0) == 0xc0) {
colorA = in.readShort();
}
colorB = in.readShort();
color4[0] = colorB;
color4[1] = 0;
color4[2] = 0;
color4[3] = colorA;
ta = (colorA >> 10) & 0x1f;
tb = (colorB >> 10) & 0x1f;
color4[1] |= ((11*ta + 21*tb) >> 5) << 10;
color4[2] |= ((21*ta + 11*tb) >> 5) << 10;
ta = (colorA >> 5) & 0x1f;
tb = (colorB >> 5) & 0x1f;
color4[1] |= ((11*ta + 21*tb) >> 5) << 5;
color4[2] |= ((21*ta + 11*tb) >> 5) << 5;
ta = colorA & 0x1f;
tb = colorB & 0x1f;
color4[1] |= (11*ta + 21*tb) >> 5;
color4[2] |= (21*ta + 11*tb) >> 5;
while (nBlocks-- > 0) {
blockPtr = rowPtr + pixelPtr;
for (pixelY=0; pixelY<4; pixelY++) {
if (in.getFilePointer() >= in.length()) break;
index = in.read();
for (pixelX=0; pixelX<4; pixelX++) {
idx = (index >> (2*(3 - pixelX))) & 3;
if (blockPtr >= pixels.length) break;
pixels[blockPtr] = color4[idx];
short s = (short) (pixels[blockPtr] & 0x7fff);
unpack(s, rtn, blockPtr, pixels.length);
blockPtr++;
}
blockPtr += rowInc;
}
updateBlock(options.width);
}
break;
case 0x00:
blockPtr = rowPtr + pixelPtr;
for (pixelY=0; pixelY<4; pixelY++) {
for (pixelX=0; pixelX<4; pixelX++) {
if ((pixelY != 0) || (pixelX != 0)) {
if (in.getFilePointer() + 2 >= in.length()) break;
colorA = in.readShort();
}
if (blockPtr >= pixels.length) break;
pixels[blockPtr] = colorA;
short s = (short) (pixels[blockPtr] & 0x7fff);
unpack(s, rtn, blockPtr, pixels.length);
blockPtr++;
}
blockPtr += rowInc;
}
updateBlock(options.width);
break;
}
}
return rtn;
}
// -- Helper methods --
private void unpack(short s, byte[] array, int offset, int len) {
array[offset] = (byte) (255 - ((s & 0x7c00) >> 10));
array[offset + len] = (byte) (255 - ((s & 0x3e0) >> 5));
array[offset + 2*len] = (byte) (255 - (s & 0x1f));
}
private void updateBlock(int width) {
pixelPtr += 4;
if (pixelPtr >= width) {
pixelPtr = 0;
rowPtr += stride * 4;
}
totalBlocks--;
}
}