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