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