4e6e638bfbb5a0f46ec00cae29e88d32ab67d2a2
[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                         if (currentCheckPointer > 0)
101                                 bitCountForRange[i] = bitcount[currentCheckPointer - 1];
102                         else
103                                 bitCountForRange[i] = 0;
104
105                 }
106
107                 for (int i = 0; i < 256; i++) {
108
109                         int seek;
110                         seekLoop: {
111                                 for (seek = 0; seek < usedEntries; seek++)
112                                         if (switchTreshold[seek] >= i)
113                                                 break seekLoop;
114                         }
115
116                         proposedRangeForActualRange[i] = range[seek];
117                         if (seek == 0)
118                                 proposedRangeForActualRangeLow[i] = 0;
119                         else
120                                 proposedRangeForActualRangeLow[i] = switchTreshold[seek - 1] + 1;
121                         proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
122                 }
123
124                 currentCheckPointer = usedEntries - 2;
125                 for (int i = 255; i >= 0; i--) {
126                         if (range[currentCheckPointer] == i)
127                                 currentCheckPointer--;
128
129                         if (currentCheckPointer < 0)
130                                 proposedDecreasedRange[i] = 0;
131                         else
132                                 proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
133                 }
134
135         }
136
137         public void load(final BitInputStream inputStream) throws IOException {
138                 reset();
139
140                 final int availableEntries = ImageDecoder
141                                 .readIntegerCompressed8(inputStream);
142
143                 for (int i = 0; i < availableEntries; i++)
144                         addEntry(inputStream.readBits(8), inputStream.readBits(8),
145                                         inputStream.readBits(4));
146         }
147
148         public int proposeBitcountForRange(int range) {
149                 if (range > 255)
150                         range = 255;
151                 if (range < 0)
152                         range = 0;
153                 final int proposal = bitCountForRange[range];
154                 return proposal;
155         }
156
157         public int proposeDecreasedRange(int range) {
158                 if (range > 255)
159                         range = 255;
160                 if (range < 0)
161                         range = 0;
162
163                 return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
164         }
165
166         public int proposeRangeForRange(final int actualRange, int inheritedRange) {
167
168                 if (inheritedRange > 255)
169                         inheritedRange = 255;
170                 if (inheritedRange < 0)
171                         inheritedRange = 0;
172
173                 if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
174                         return inheritedRange;
175
176                 return proposeDecreasedRange(inheritedRange);
177         }
178
179         public void reset() {
180                 range = new int[100];
181                 switchTreshold = new int[100];
182                 bitcount = new int[100];
183
184                 bitCountForRange = new int[256];
185                 proposedRangeForActualRange = new int[256];
186                 proposedRangeForActualRangeLow = new int[256];
187                 proposedRangeForActualRangeHigh = new int[256];
188                 proposedDecreasedRange = new byte[256];
189
190                 usedEntries = 0;
191         }
192
193         public void save(final BitOutputStream outputStream) throws IOException {
194                 ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
195
196                 for (int i = 0; i < usedEntries; i++) {
197                         outputStream.storeBits(range[i], 8);
198                         outputStream.storeBits(switchTreshold[i], 8);
199                         outputStream.storeBits(bitcount[i], 4);
200                 }
201         }
202
203 }