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