Changed license to CC0
[imagesqueeze.git] / src / main / java / eu / svjatoslav / imagesqueeze / codec / Table.java
1 /*
2  * Image codec. Author: Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
3  * This project is released under Creative Commons Zero (CC0) license.
4  */
5 package eu.svjatoslav.imagesqueeze.codec;
6
7 import eu.svjatoslav.commons.data.BitInputStream;
8 import eu.svjatoslav.commons.data.BitOutputStream;
9
10 import java.io.IOException;
11
12 /**
13  * Quick lookup table.
14  */
15
16 public class Table {
17
18     private int[] range = new int[100];
19     private int[] switchThreshold = new int[100];
20     private int[] bitCount = new int[100];
21
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];
27
28     private int usedEntries = 0;
29
30     /**
31      * @param switchThreshold - switch to this range when actual range in equal or below
32      *                        this threshold
33      */
34     public void addEntry(int range, int switchThreshold, int bitcount) {
35         if (range < 0)
36             range = 0;
37         if (range > 255)
38             range = 255;
39
40         if (switchThreshold < 0)
41             switchThreshold = 0;
42         if (switchThreshold > 255)
43             switchThreshold = 255;
44
45         if (bitcount < 0)
46             bitcount = 0;
47         if (bitcount > 8)
48             bitcount = 8;
49
50         this.range[usedEntries] = range;
51         this.switchThreshold[usedEntries] = switchThreshold;
52         this.bitCount[usedEntries] = bitcount;
53         usedEntries++;
54     }
55
56     public void computeLookupTables() {
57         int currentCheckPointer = 0;
58
59         for (int i = 0; i < 256; i++) {
60
61             if (range[currentCheckPointer] == i)
62                 currentCheckPointer++;
63
64             if (currentCheckPointer > 0)
65                 bitCountForRange[i] = bitCount[currentCheckPointer - 1];
66             else
67                 bitCountForRange[i] = 0;
68
69         }
70
71         for (int i = 0; i < 256; i++) {
72
73             int seek;
74             seekLoop:
75             {
76                 for (seek = 0; seek < usedEntries; seek++)
77                     if (switchThreshold[seek] >= i)
78                         break seekLoop;
79             }
80
81             proposedRangeForActualRange[i] = range[seek];
82             if (seek == 0)
83                 proposedRangeForActualRangeLow[i] = 0;
84             else
85                 proposedRangeForActualRangeLow[i] = switchThreshold[seek - 1] + 1;
86             proposedRangeForActualRangeHigh[i] = switchThreshold[seek];
87         }
88
89         currentCheckPointer = usedEntries - 2;
90         for (int i = 255; i >= 0; i--) {
91             if (range[currentCheckPointer] == i)
92                 currentCheckPointer--;
93
94             if (currentCheckPointer < 0)
95                 proposedDecreasedRange[i] = 0;
96             else
97                 proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
98         }
99
100     }
101
102     public void load(final BitInputStream inputStream) throws IOException {
103         reset();
104
105         final int availableEntries = ImageDecoder
106                 .readIntegerCompressed8(inputStream);
107
108         for (int i = 0; i < availableEntries; i++)
109             addEntry(inputStream.readBits(8), inputStream.readBits(8),
110                     inputStream.readBits(4));
111     }
112
113     public int proposeBitcountForRange(int range) {
114         if (range > 255)
115             range = 255;
116         if (range < 0)
117             range = 0;
118         return bitCountForRange[range];
119     }
120
121     public int proposeDecreasedRange(int range) {
122         if (range > 255)
123             range = 255;
124         if (range < 0)
125             range = 0;
126
127         return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
128     }
129
130     public int proposeRangeForRange(final int actualRange, int inheritedRange) {
131
132         if (inheritedRange > 255)
133             inheritedRange = 255;
134         if (inheritedRange < 0)
135             inheritedRange = 0;
136
137         if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
138             return inheritedRange;
139
140         return proposeDecreasedRange(inheritedRange);
141     }
142
143     public void reset() {
144         range = new int[100];
145         switchThreshold = new int[100];
146         bitCount = new int[100];
147
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];
153
154         usedEntries = 0;
155     }
156
157     public void save(final BitOutputStream outputStream) throws IOException {
158         ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
159
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);
164         }
165     }
166
167 }