1 package eu.svjatoslav.imagesqueeze.codec;
4 * Compressed image pixels decoder.
7 import java.awt.image.DataBufferByte;
8 import java.awt.image.WritableRaster;
9 import java.io.IOException;
11 public class ImageDecoder {
16 byte [] decodedYRangeMap;
19 byte [] decodedURangeMap;
22 byte [] decodedVRangeMap;
25 Color tmpColor = new Color();
27 Approximator approximator;
28 BitInputStream bitInputStream;
30 ColorStats colorStats = new ColorStats();
31 OperatingContext context = new OperatingContext();
33 public ImageDecoder (Image image, BitInputStream bitInputStream) {
34 approximator = new Approximator();
37 this.bitInputStream = bitInputStream;
39 width = image.metaData.width;
40 height = image.metaData.height;
42 decodedYRangeMap = new byte[width * height];
43 decodedYRangeMap[0] = (byte)(255);
44 decodedYMap = new byte[width * height];
46 decodedURangeMap = new byte[width * height];
47 decodedURangeMap[0] = (byte)(255);
48 decodedUMap = new byte[width * height];
50 decodedVRangeMap = new byte[width * height];
51 decodedVRangeMap[0] = (byte)(255);
52 decodedVMap = new byte[width * height];
57 public void decode() throws IOException {
58 approximator.load(bitInputStream);
59 approximator.computeLookupTables();
61 WritableRaster raster = image.bufferedImage.getRaster();
62 DataBufferByte dbi = (DataBufferByte)raster.getDataBuffer();
63 byte [] pixels = dbi.getData();
65 // load top-, left-most pixel.
66 decodedYMap[0] = (byte)bitInputStream.readBits(8);
67 decodedUMap[0] = (byte)bitInputStream.readBits(8);
68 decodedVMap[0] = (byte)bitInputStream.readBits(8);
70 Color color = new Color();
71 color.y = ImageEncoder.byteToInt(decodedYMap[0]);
72 color.u = ImageEncoder.byteToInt(decodedUMap[0]);
73 color.v = ImageEncoder.byteToInt(decodedVMap[0]);
77 pixels[0] = (byte)color.r;
78 pixels[0+1] = (byte)color.g;
79 pixels[0+2] = (byte)color.b;
82 // detect initial step
86 largestDimension = width;
88 largestDimension = height;
91 while (initialStep < largestDimension){
92 initialStep = initialStep * 2;
95 grid(initialStep, pixels);
99 public void grid(int step, byte [] pixels) throws IOException {
101 gridDiagonal(step / 2, step / 2, step, pixels);
102 gridSquare(step / 2, 0, step, pixels);
103 gridSquare(0, step / 2, step, pixels);
105 if (step > 2) grid(step / 2, pixels);
109 public void gridSquare(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{
111 for (int y = offsetY; y < height; y = y + step){
112 for (int x = offsetX; x < width; x = x + step){
115 int halfStep = step / 2;
117 context.initialize(image, decodedYMap, decodedUMap, decodedVMap);
118 context.measureNeighborEncode(x - halfStep, y);
119 context.measureNeighborEncode(x + halfStep, y);
120 context.measureNeighborEncode(x, y - halfStep);
121 context.measureNeighborEncode(x, y + halfStep);
123 loadPixel(step, offsetX, offsetY, x, y, pixels,
124 context.colorStats.getAverageY(),
125 context.colorStats.getAverageU(),
126 context.colorStats.getAverageV());
133 public void gridDiagonal(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{
135 for (int y = offsetY; y < height; y = y + step){
136 for (int x = offsetX; x < width; x = x + step){
138 int halfStep = step / 2;
140 context.initialize(image, decodedYMap, decodedUMap, decodedVMap);
141 context.measureNeighborEncode(x - halfStep, y - halfStep);
142 context.measureNeighborEncode(x + halfStep, y - halfStep);
143 context.measureNeighborEncode(x - halfStep, y + halfStep);
144 context.measureNeighborEncode(x + halfStep, y + halfStep);
146 loadPixel(step, offsetX, offsetY, x, y, pixels,
147 context.colorStats.getAverageY(),
148 context.colorStats.getAverageU(),
149 context.colorStats.getAverageV());
156 public void loadPixel(int step, int offsetX, int offsetY, int x, int y, byte[] pixels,
157 int averageDecodedY, int averageDecodedU, int averageDecodedV)
160 int index = (y * width) + x;
162 int halfStep = step / 2;
168 parentIndex = ((y - halfStep) * width) + (x - halfStep);
171 parentIndex = (y * width) + (x - halfStep);
175 parentIndex = ((y - halfStep) * width) + x;
180 int colorBufferIndex = index * 3;
182 Color color = new Color();
183 color.y = loadChannel(decodedYRangeMap, decodedYMap, approximator.yTable, averageDecodedY, index, parentIndex);
184 color.u = loadChannel(decodedURangeMap, decodedUMap, approximator.uTable, averageDecodedU, index, parentIndex);
185 color.v = loadChannel(decodedVRangeMap, decodedVMap, approximator.vTable, averageDecodedV, index, parentIndex);
189 pixels[colorBufferIndex] = (byte)color.r;
190 pixels[colorBufferIndex+1] = (byte)color.g;
191 pixels[colorBufferIndex+2] = (byte)color.b;
196 private int loadChannel(byte [] decodedRangeMap, byte [] decodedMap, Table table, int averageDecodedValue, int index,
197 int parentIndex) throws IOException {
198 int decodedValue = averageDecodedValue;
200 int inheritedRange = ImageEncoder.byteToInt(decodedRangeMap[parentIndex]);
201 int computedRange = inheritedRange;
203 int bitCount = table.proposeBitcountForRange(inheritedRange);
204 int computedRangeBitCount = 0;
207 int rangeDecreases = bitInputStream.readBits(1);
208 if (rangeDecreases != 0){
209 computedRange = table.proposeDecreasedRange(inheritedRange);
212 decodedRangeMap[index] = (byte)computedRange;
213 computedRangeBitCount = table.proposeBitcountForRange(computedRange);
215 if (computedRangeBitCount > 0){
217 int encodedDifference = bitInputStream.readBits(computedRangeBitCount);
219 int decodedDifference = ImageEncoder.decodeValueFromGivenBits(encodedDifference, computedRange, computedRangeBitCount);
221 decodedValue = averageDecodedValue - decodedDifference;
222 if (decodedValue > 255) decodedValue = 255;
223 if (decodedValue < 0) decodedValue = 0;
226 decodedRangeMap[index] = (byte)inheritedRange;
228 decodedMap[index] = (byte)decodedValue;