/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.imaging.common; import java.io.ByteArrayOutputStream; import java.io.IOException; import org.apache.commons.imaging.ImageReadException; public class PackBits { public byte[] decompress(final byte bytes[], final int expected) throws ImageReadException { int total = 0; final ByteArrayOutputStream baos = new ByteArrayOutputStream(); // Loop until you get the number of unpacked bytes you are expecting: int i = 0; while (total < expected) { // Read the next source byte into n. if (i >= bytes.length) { throw new ImageReadException( "Tiff: Unpack bits source exhausted: " + i + ", done + " + total + ", expected + " + expected); } final int n = bytes[i++]; if ((n >= 0) && (n <= 127)) { // If n is between 0 and 127 inclusive, copy the next n+1 bytes // literally. final int count = n + 1; total += count; for (int j = 0; j < count; j++) { baos.write(bytes[i++]); } } else if ((n >= -127) && (n <= -1)) { // Else if n is between -127 and -1 inclusive, copy the next byte // -n+1 times. final int b = bytes[i++]; final int count = -n + 1; total += count; for (int j = 0; j < count; j++) { baos.write(b); } } else if (n == -128) { // Else if n is -128, noop. throw new ImageReadException("Packbits: " + n); } } final byte result[] = baos.toByteArray(); return result; } private int findNextDuplicate(final byte bytes[], final int start) { // int last = -1; if (start >= bytes.length) { return -1; } byte prev = bytes[start]; for (int i = start + 1; i < bytes.length; i++) { final byte b = bytes[i]; if (b == prev) { return i - 1; } prev = b; } return -1; } private int findRunLength(final byte bytes[], final int start) { final byte b = bytes[start]; int i; for (i = start + 1; (i < bytes.length) && (bytes[i] == b); i++) { // do nothing } return i - start; } public byte[] compress(final byte bytes[]) throws IOException { FastByteArrayOutputStream baos = null; try { baos = new FastByteArrayOutputStream( bytes.length * 2); // max length 1 extra byte for every 128 int ptr = 0; while (ptr < bytes.length) { int dup = findNextDuplicate(bytes, ptr); if (dup == ptr) { // write run length final int len = findRunLength(bytes, dup); final int actual_len = Math.min(len, 128); baos.write(-(actual_len - 1)); baos.write(bytes[ptr]); ptr += actual_len; } else { // write literals int len = dup - ptr; if (dup > 0) { final int runlen = findRunLength(bytes, dup); if (runlen < 3) { // may want to discard next run. final int nextptr = ptr + len + runlen; final int nextdup = findNextDuplicate(bytes, nextptr); if (nextdup != nextptr) { // discard 2-byte run dup = nextdup; len = dup - ptr; } } } if (dup < 0) { len = bytes.length - ptr; } final int actual_len = Math.min(len, 128); baos.write(actual_len - 1); for (int i = 0; i < actual_len; i++) { baos.write(bytes[ptr]); ptr++; } } } final byte result[] = baos.toByteArray(); return result; } finally { if (baos != null) { baos.close(); } } } }