/* * 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); } } }