2 * Image codec. Author: Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
3 * This project is released under Creative Commons Zero (CC0) license.
5 package eu.svjatoslav.imagesqueeze.codec;
7 import eu.svjatoslav.commons.data.BitInputStream;
8 import eu.svjatoslav.commons.data.BitOutputStream;
10 import java.io.IOException;
18 private int[] range = new int[100];
19 private int[] switchThreshold = new int[100];
20 private int[] bitCount = new int[100];
22 private int[] bitCountForRange = new int[256];
23 private int[] proposedRangeForActualRange = new int[256];
24 private int[] proposedRangeForActualRangeLow = new int[256];
25 private int[] proposedRangeForActualRangeHigh = new int[256];
26 private byte[] proposedDecreasedRange = new byte[256];
28 private int usedEntries = 0;
31 * @param switchThreshold - switch to this range when actual range in equal or below
34 public void addEntry(int range, int switchThreshold, int bitcount) {
40 if (switchThreshold < 0)
42 if (switchThreshold > 255)
43 switchThreshold = 255;
50 this.range[usedEntries] = range;
51 this.switchThreshold[usedEntries] = switchThreshold;
52 this.bitCount[usedEntries] = bitcount;
56 public void computeLookupTables() {
57 int currentCheckPointer = 0;
59 for (int i = 0; i < 256; i++) {
61 if (range[currentCheckPointer] == i)
62 currentCheckPointer++;
64 if (currentCheckPointer > 0)
65 bitCountForRange[i] = bitCount[currentCheckPointer - 1];
67 bitCountForRange[i] = 0;
71 for (int i = 0; i < 256; i++) {
76 for (seek = 0; seek < usedEntries; seek++)
77 if (switchThreshold[seek] >= i)
81 proposedRangeForActualRange[i] = range[seek];
83 proposedRangeForActualRangeLow[i] = 0;
85 proposedRangeForActualRangeLow[i] = switchThreshold[seek - 1] + 1;
86 proposedRangeForActualRangeHigh[i] = switchThreshold[seek];
89 currentCheckPointer = usedEntries - 2;
90 for (int i = 255; i >= 0; i--) {
91 if (range[currentCheckPointer] == i)
92 currentCheckPointer--;
94 if (currentCheckPointer < 0)
95 proposedDecreasedRange[i] = 0;
97 proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
102 public void load(final BitInputStream inputStream) throws IOException {
105 final int availableEntries = ImageDecoder
106 .readIntegerCompressed8(inputStream);
108 for (int i = 0; i < availableEntries; i++)
109 addEntry(inputStream.readBits(8), inputStream.readBits(8),
110 inputStream.readBits(4));
113 public int proposeBitcountForRange(int range) {
118 return bitCountForRange[range];
121 public int proposeDecreasedRange(int range) {
127 return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
130 public int proposeRangeForRange(final int actualRange, int inheritedRange) {
132 if (inheritedRange > 255)
133 inheritedRange = 255;
134 if (inheritedRange < 0)
137 if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
138 return inheritedRange;
140 return proposeDecreasedRange(inheritedRange);
143 public void reset() {
144 range = new int[100];
145 switchThreshold = new int[100];
146 bitCount = new int[100];
148 bitCountForRange = new int[256];
149 proposedRangeForActualRange = new int[256];
150 proposedRangeForActualRangeLow = new int[256];
151 proposedRangeForActualRangeHigh = new int[256];
152 proposedDecreasedRange = new byte[256];
157 public void save(final BitOutputStream outputStream) throws IOException {
158 ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
160 for (int i = 0; i < usedEntries; i++) {
161 outputStream.storeBits(range[i], 8);
162 outputStream.storeBits(switchThreshold[i], 8);
163 outputStream.storeBits(bitCount[i], 4);