/*
* Imagesqueeze - Image codec optimized for photos.
* Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*/
package eu.svjatoslav.imagesqueeze.codec;
import java.io.IOException;
import eu.svjatoslav.commons.data.BitInputStream;
import eu.svjatoslav.commons.data.BitOutputStream;
/**
* Quick lookup table.
*/
public class Table implements Comparable
{
int[] range = new int[100];
int[] switchTreshold = new int[100];
int[] bitcount = new int[100];
int[] bitCountForRange = new int[256];
int[] proposedRangeForActualRange = new int[256];
int[] proposedRangeForActualRangeLow = new int[256];
int[] proposedRangeForActualRangeHigh = new int[256];
byte[] proposedDecreasedRange = new byte[256];
int usedEntries = 0;
/**
* @param switchTreshold
* - switch to this range when actual range in equal or below
* this treshold
*/
public void addEntry(int range, int switchTreshold, int bitcount) {
if (range < 0)
range = 0;
if (range > 255)
range = 255;
if (switchTreshold < 0)
switchTreshold = 0;
if (switchTreshold > 255)
switchTreshold = 255;
if (bitcount < 0)
bitcount = 0;
if (bitcount > 8)
bitcount = 8;
this.range[usedEntries] = range;
this.switchTreshold[usedEntries] = switchTreshold;
this.bitcount[usedEntries] = bitcount;
usedEntries++;
}
/**
* Compares two tables. Ignores table initialization.
*/
@Override
public int compareTo(final Table o) {
if (usedEntries < o.usedEntries)
return -1;
if (usedEntries > o.usedEntries)
return 1;
for (int i = 0; i < usedEntries; i++) {
if (range[i] < o.range[i])
return -1;
if (range[i] > o.range[i])
return 1;
if (switchTreshold[i] < o.switchTreshold[i])
return -1;
if (switchTreshold[i] > o.switchTreshold[i])
return 1;
if (bitcount[i] < o.bitcount[i])
return -1;
if (bitcount[i] > o.bitcount[i])
return 1;
}
return 0;
}
public void computeLookupTables() {
int currentCheckPointer = 0;
for (int i = 0; i < 256; i++) {
if (range[currentCheckPointer] == i) {
currentCheckPointer++;
}
if (currentCheckPointer > 0) {
bitCountForRange[i] = bitcount[currentCheckPointer - 1];
} else {
bitCountForRange[i] = 0;
}
}
for (int i = 0; i < 256; i++) {
int seek;
seekLoop: {
for (seek = 0; seek < usedEntries; seek++) {
if (switchTreshold[seek] >= i)
break seekLoop;
}
}
proposedRangeForActualRange[i] = range[seek];
if (seek == 0) {
proposedRangeForActualRangeLow[i] = 0;
} else {
proposedRangeForActualRangeLow[i] = switchTreshold[seek - 1] + 1;
}
proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
}
currentCheckPointer = usedEntries - 2;
for (int i = 255; i >= 0; i--) {
if (range[currentCheckPointer] == i)
currentCheckPointer--;
if (currentCheckPointer < 0) {
proposedDecreasedRange[i] = 0;
} else {
proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
}
}
}
public void load(final BitInputStream inputStream) throws IOException {
reset();
final int availableEntries = inputStream.readIntegerCompressed8();
for (int i = 0; i < availableEntries; i++) {
addEntry(inputStream.readBits(8), inputStream.readBits(8),
inputStream.readBits(4));
}
}
public int proposeBitcountForRange(int range) {
if (range > 255)
range = 255;
if (range < 0)
range = 0;
final int proposal = bitCountForRange[range];
return proposal;
}
public int proposeDecreasedRange(int range) {
if (range > 255)
range = 255;
if (range < 0)
range = 0;
return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
}
public int proposeRangeForRange(final int actualRange, int inheritedRange) {
if (inheritedRange > 255)
inheritedRange = 255;
if (inheritedRange < 0)
inheritedRange = 0;
if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange) {
return inheritedRange;
}
return proposeDecreasedRange(inheritedRange);
}
public void reset() {
range = new int[100];
switchTreshold = new int[100];
bitcount = new int[100];
bitCountForRange = new int[256];
proposedRangeForActualRange = new int[256];
proposedRangeForActualRangeLow = new int[256];
proposedRangeForActualRangeHigh = new int[256];
proposedDecreasedRange = new byte[256];
usedEntries = 0;
}
public void save(final BitOutputStream outputStream) throws IOException {
outputStream.storeIntegerCompressed8(usedEntries);
for (int i = 0; i < usedEntries; i++) {
outputStream.storeBits(range[i], 8);
outputStream.storeBits(switchTreshold[i], 8);
outputStream.storeBits(bitcount[i], 4);
}
}
}