Moved bit input and output streams into Svjatoslav Commons library.
[imagesqueeze.git] / src / main / java / eu / svjatoslav / imagesqueeze / codec / ImageEncoder.java
index 46c2135..613064c 100755 (executable)
@@ -1,3 +1,12 @@
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
 package eu.svjatoslav.imagesqueeze.codec;
 
 /**
@@ -8,6 +17,7 @@ import java.awt.image.DataBufferByte;
 import java.awt.image.WritableRaster;
 import java.io.IOException;
 
+import eu.svjatoslav.commons.data.BitOutputStream;
 
 public class ImageEncoder {
 
@@ -17,78 +27,81 @@ public class ImageEncoder {
        Channel yChannel;
        Channel uChannel;
        Channel vChannel;
-       
+
        Approximator approximator;
 
        int bitsForY;
        int bitsForU;
        int bitsForV;
-       
-       //ColorStats colorStats = new ColorStats();
+
+       // ColorStats colorStats = new ColorStats();
        OperatingContext context = new OperatingContext();
        OperatingContext context2 = new OperatingContext();
 
        BitOutputStream bitOutputStream;
 
-       public ImageEncoder(Image image){
+       public ImageEncoder(final Image image) {
                approximator = new Approximator();
 
-               //bitOutputStream = outputStream;
+               // bitOutputStream = outputStream;
 
                this.image = image;
 
        }
 
-
-       public void encode(BitOutputStream bitOutputStream) throws IOException {
+       public void encode(final BitOutputStream bitOutputStream)
+                       throws IOException {
                this.bitOutputStream = bitOutputStream;
-               
+
                approximator.initialize();
-               
+
                approximator.save(bitOutputStream);
-               
+
                width = image.metaData.width;
                height = image.metaData.height;
 
-               WritableRaster raster = image.bufferedImage.getRaster();
-               DataBufferByte dbi = (DataBufferByte)raster.getDataBuffer();
-               byte [] pixels = dbi.getData();
+               final WritableRaster raster = image.bufferedImage.getRaster();
+               final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+               final byte[] pixels = dbi.getData();
 
-               if (yChannel == null){
-                       yChannel = new Channel(width, height);                  
+               if (yChannel == null) {
+                       yChannel = new Channel(width, height);
                } else {
                        yChannel.reset();
                }
-               
-               if (uChannel == null){
-                       uChannel = new Channel(width, height);                  
+
+               if (uChannel == null) {
+                       uChannel = new Channel(width, height);
                } else {
                        uChannel.reset();
                }
-               
-               if (vChannel == null){
-                       vChannel = new Channel(width, height);                  
+
+               if (vChannel == null) {
+                       vChannel = new Channel(width, height);
                } else {
                        vChannel.reset();
                }
-               
+
                // create YUV map out of RGB raster data
-               Color color = new Color();
-               
-               for (int y=0; y < height; y++){
-                       for (int x=0; x < width; x++){
+               final Color color = new Color();
+
+               for (int y = 0; y < height; y++) {
+                       for (int x = 0; x < width; x++) {
 
-                               int index = (y * width) + x;
-                               int colorBufferIndex = index * 3;
+                               final int index = (y * width) + x;
+                               final int colorBufferIndex = index * 3;
 
-                               int blue = pixels[colorBufferIndex];                            
-                               if (blue < 0) blue = blue + 256;
+                               int blue = pixels[colorBufferIndex];
+                               if (blue < 0)
+                                       blue = blue + 256;
 
-                               int green = pixels[colorBufferIndex+1];
-                               if (green < 0) green = green + 256;
+                               int green = pixels[colorBufferIndex + 1];
+                               if (green < 0)
+                                       green = green + 256;
 
-                               int red = pixels[colorBufferIndex+2];
-                               if (red < 0) red = red + 256;
+                               int red = pixels[colorBufferIndex + 2];
+                               if (red < 0)
+                                       red = red + 256;
 
                                color.r = red;
                                color.g = green;
@@ -96,19 +109,19 @@ public class ImageEncoder {
 
                                color.RGB2YUV();
 
-                               yChannel.map[index] = (byte)color.y;
-                               uChannel.map[index] = (byte)color.u;
-                               vChannel.map[index] = (byte)color.v;
+                               yChannel.map[index] = (byte) color.y;
+                               uChannel.map[index] = (byte) color.u;
+                               vChannel.map[index] = (byte) color.v;
                        }
                }
 
                yChannel.decodedMap[0] = yChannel.map[0];
                uChannel.decodedMap[0] = uChannel.map[0];
                vChannel.decodedMap[0] = vChannel.map[0];
-               
-               bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);                                                               
-               bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);                                                               
-               bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);                                                               
+
+               bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);
+               bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);
+               bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);
 
                // detect initial step
                int largestDimension;
@@ -118,364 +131,413 @@ public class ImageEncoder {
                } else {
                        largestDimension = height;
                }
-               
-               while (initialStep < largestDimension){
+
+               while (initialStep < largestDimension) {
                        initialStep = initialStep * 2;
                }
-                               
+
                rangeGrid(initialStep);
                rangeRoundGrid(2);
                saveGrid(initialStep);
        }
 
-       public void printStatistics(){
+       private void encodeChannel(final Table table, final Channel channel,
+                       final int averageDecodedValue, final int index, final int value,
+                       final int range, final int parentIndex) throws IOException {
+
+               final byte[] decodedRangeMap = channel.decodedRangeMap;
+               final byte[] decodedMap = channel.decodedMap;
+
+               final int inheritedRange = byteToInt(decodedRangeMap[parentIndex]);
+
+               final int inheritedBitCount = table
+                               .proposeBitcountForRange(inheritedRange);
+
+               if (inheritedBitCount > 0) {
+                       int computedRange;
+                       computedRange = table.proposeRangeForRange(range, inheritedRange);
+                       decodedRangeMap[index] = (byte) computedRange;
+
+                       channel.bitCount++;
+                       if (computedRange != inheritedRange) {
+                               // brightness range shrinked
+                               bitOutputStream.storeBits(1, 1);
+                       } else {
+                               // brightness range stayed the same
+                               bitOutputStream.storeBits(0, 1);
+                       }
+
+                       // encode brightness into available amount of bits
+                       final int computedBitCount = table
+                                       .proposeBitcountForRange(computedRange);
+
+                       if (computedBitCount > 0) {
+
+                               final int differenceToEncode = -(value - averageDecodedValue);
+                               final int bitEncodedDifference = encodeValueIntoGivenBits(
+                                               differenceToEncode, computedRange, computedBitCount);
+
+                               channel.bitCount = channel.bitCount + computedBitCount;
+                               bitOutputStream.storeBits(bitEncodedDifference,
+                                               computedBitCount);
+
+                               final int decodedDifference = decodeValueFromGivenBits(
+                                               bitEncodedDifference, computedRange, computedBitCount);
+                               int decodedValue = averageDecodedValue - decodedDifference;
+                               if (decodedValue > 255)
+                                       decodedValue = 255;
+                               if (decodedValue < 0)
+                                       decodedValue = 0;
+
+                               decodedMap[index] = (byte) decodedValue;
+                       } else {
+                               decodedMap[index] = (byte) averageDecodedValue;
+                       }
+
+               } else {
+                       decodedRangeMap[index] = (byte) inheritedRange;
+                       decodedMap[index] = (byte) averageDecodedValue;
+               }
+       }
+
+       public void printStatistics() {
                System.out.println("Y channel:");
                yChannel.printStatistics();
 
                System.out.println("U channel:");
                uChannel.printStatistics();
-               
+
                System.out.println("V channel:");
-               vChannel.printStatistics();             
+               vChannel.printStatistics();
        }
-       
-       public void rangeGrid(int step){
 
-               //gridSquare(step / 2, step / 2, step, pixels);
+       public void rangeGrid(final int step) {
+
+               // gridSquare(step / 2, step / 2, step, pixels);
 
                rangeGridDiagonal(step / 2, step / 2, step);
                rangeGridSquare(step / 2, 0, step);
                rangeGridSquare(0, step / 2, step);
 
-               if (step > 2) rangeGrid(step / 2);
+               if (step > 2)
+                       rangeGrid(step / 2);
        }
 
+       public void rangeGridDiagonal(final int offsetX, final int offsetY,
+                       final int step) {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-       public void rangeRoundGrid(int step){
-
-               rangeRoundGridDiagonal(step / 2, step / 2, step);
-               rangeRoundGridSquare(step / 2, 0, step);
-               rangeRoundGridSquare(0, step / 2, step);
-
-               if (step < 1024) rangeRoundGrid(step * 2);
-       }
+                               final int index = (y * width) + x;
+                               final int halfStep = step / 2;
 
-       public void saveGrid(int step) throws IOException {
+                               context.initialize(image, yChannel.map, uChannel.map,
+                                               vChannel.map);
 
-               saveGridDiagonal(step / 2, step / 2, step);
-               saveGridSquare(step / 2, 0, step);
-               saveGridSquare(0, step / 2, step);
+                               context.measureNeighborEncode(x - halfStep, y - halfStep);
+                               context.measureNeighborEncode(x + halfStep, y - halfStep);
+                               context.measureNeighborEncode(x - halfStep, y + halfStep);
+                               context.measureNeighborEncode(x + halfStep, y + halfStep);
 
-               if (step > 2) saveGrid(step / 2);
+                               yChannel.rangeMap[index] = (byte) context.getYRange(index);
+                               uChannel.rangeMap[index] = (byte) context.getURange(index);
+                               vChannel.rangeMap[index] = (byte) context.getVRange(index);
+                       }
+               }
        }
 
+       public void rangeGridSquare(final int offsetX, final int offsetY,
+                       final int step) {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-       public void rangeGridSquare(int offsetX, int offsetY, int step){
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
+                               final int index = (y * width) + x;
+                               final int halfStep = step / 2;
 
-                               int index = (y * width) + x;
-                               int halfStep = step / 2;
+                               context.initialize(image, yChannel.map, uChannel.map,
+                                               vChannel.map);
 
-                               context.initialize(image, yChannel.map, uChannel.map, vChannel.map);
-                       
                                context.measureNeighborEncode(x - halfStep, y);
                                context.measureNeighborEncode(x + halfStep, y);
                                context.measureNeighborEncode(x, y - halfStep);
                                context.measureNeighborEncode(x, y + halfStep);
 
-                               yChannel.rangeMap[index] = (byte)context.getYRange(index);
-                               uChannel.rangeMap[index] = (byte)context.getURange(index);
-                               vChannel.rangeMap[index] = (byte)context.getVRange(index);
-                       }                       
-               }               
+                               yChannel.rangeMap[index] = (byte) context.getYRange(index);
+                               uChannel.rangeMap[index] = (byte) context.getURange(index);
+                               vChannel.rangeMap[index] = (byte) context.getVRange(index);
+                       }
+               }
        }
 
-       public void rangeGridDiagonal(int offsetX, int offsetY, int step){
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
+       public void rangeRoundGrid(final int step) {
 
-                               int index = (y * width) + x;
-                               int halfStep = step / 2;
-
-                               context.initialize(image, yChannel.map, uChannel.map, vChannel.map);
-                               
-                               context.measureNeighborEncode(x - halfStep, y - halfStep);
-                               context.measureNeighborEncode(x + halfStep, y - halfStep);
-                               context.measureNeighborEncode(x - halfStep, y + halfStep);
-                               context.measureNeighborEncode(x + halfStep, y + halfStep);
+               rangeRoundGridDiagonal(step / 2, step / 2, step);
+               rangeRoundGridSquare(step / 2, 0, step);
+               rangeRoundGridSquare(0, step / 2, step);
 
-                               yChannel.rangeMap[index] = (byte)context.getYRange(index);
-                               uChannel.rangeMap[index] = (byte)context.getURange(index);
-                               vChannel.rangeMap[index] = (byte)context.getVRange(index);
-                       }                       
-               }               
+               if (step < 1024)
+                       rangeRoundGrid(step * 2);
        }
 
-       public void rangeRoundGridDiagonal(int offsetX, int offsetY, int step){
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
+       public void rangeRoundGridDiagonal(final int offsetX, final int offsetY,
+                       final int step) {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-                               int index = (y * width) + x;
+                               final int index = (y * width) + x;
 
-                               int yRange = byteToInt(yChannel.rangeMap[index]);
-                               int uRange = byteToInt(uChannel.rangeMap[index]);
-                               int vRange = byteToInt(vChannel.rangeMap[index]);
+                               final int yRange = byteToInt(yChannel.rangeMap[index]);
+                               final int uRange = byteToInt(uChannel.rangeMap[index]);
+                               final int vRange = byteToInt(vChannel.rangeMap[index]);
 
-                               int halfStep = step / 2;
+                               final int halfStep = step / 2;
 
-                               int parentIndex = ((y - halfStep) * width) + (x - halfStep);
+                               final int parentIndex = ((y - halfStep) * width)
+                                               + (x - halfStep);
 
-                               int parentYRange =  byteToInt(yChannel.rangeMap[parentIndex]);
+                               int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
 
-                               if (parentYRange < yRange){
+                               if (parentYRange < yRange) {
                                        parentYRange = yRange;
-                                       yChannel.rangeMap[parentIndex] = (byte)parentYRange;
+                                       yChannel.rangeMap[parentIndex] = (byte) parentYRange;
                                }
 
                                int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
 
-                               if (parentURange < uRange){
+                               if (parentURange < uRange) {
                                        parentURange = uRange;
-                                       uChannel.rangeMap[parentIndex] = (byte)parentURange;
+                                       uChannel.rangeMap[parentIndex] = (byte) parentURange;
                                }
 
                                int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
 
-                               if (parentVRange < vRange){
+                               if (parentVRange < vRange) {
                                        parentVRange = vRange;
-                                       vChannel.rangeMap[parentIndex] = (byte)parentVRange;
+                                       vChannel.rangeMap[parentIndex] = (byte) parentVRange;
                                }
-                       }                       
-               }               
+                       }
+               }
        }
 
-       public void rangeRoundGridSquare(int offsetX, int offsetY, int step){
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
+       public void rangeRoundGridSquare(final int offsetX, final int offsetY,
+                       final int step) {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-                               int index = (y * width) + x;
-                               
-                               int yRange = byteToInt(yChannel.rangeMap[index]);
-                               int uRange = byteToInt(uChannel.rangeMap[index]);
-                               int vRange = byteToInt(vChannel.rangeMap[index]);
+                               final int index = (y * width) + x;
 
-                               int halfStep = step / 2;
+                               final int yRange = byteToInt(yChannel.rangeMap[index]);
+                               final int uRange = byteToInt(uChannel.rangeMap[index]);
+                               final int vRange = byteToInt(vChannel.rangeMap[index]);
+
+                               final int halfStep = step / 2;
 
                                int parentIndex;
-                               if (offsetX > 0){
-                                       parentIndex = (y * width) + (x - halfStep);                                     
+                               if (offsetX > 0) {
+                                       parentIndex = (y * width) + (x - halfStep);
                                } else {
-                                       parentIndex = ((y - halfStep) * width) + x;                                                                             
+                                       parentIndex = ((y - halfStep) * width) + x;
                                }
 
-                               int parentYRange =  byteToInt(yChannel.rangeMap[parentIndex]);
+                               int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
 
-                               if (parentYRange < yRange){
+                               if (parentYRange < yRange) {
                                        parentYRange = yRange;
-                                       yChannel.rangeMap[parentIndex] = (byte)parentYRange;
+                                       yChannel.rangeMap[parentIndex] = (byte) parentYRange;
                                }
 
                                int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
 
-                               if (parentURange < uRange){
+                               if (parentURange < uRange) {
                                        parentURange = uRange;
-                                       uChannel.rangeMap[parentIndex] = (byte)parentURange;
+                                       uChannel.rangeMap[parentIndex] = (byte) parentURange;
                                }
 
                                int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
 
-                               if (parentVRange < vRange){
+                               if (parentVRange < vRange) {
                                        parentVRange = vRange;
-                                       vChannel.rangeMap[parentIndex] = (byte)parentVRange;
+                                       vChannel.rangeMap[parentIndex] = (byte) parentVRange;
                                }
 
-                       }                       
-               }               
+                       }
+               }
        }
 
-       public void saveGridSquare(int offsetX, int offsetY, int step) throws IOException{
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
-
-                               int halfStep = step / 2;
+       public void saveGrid(final int step) throws IOException {
 
-                               context2.initialize(image, yChannel.decodedMap, uChannel.decodedMap,  vChannel.decodedMap);
-                               context2.measureNeighborEncode(x - halfStep, y);
-                               context2.measureNeighborEncode(x + halfStep, y);
-                               context2.measureNeighborEncode(x, y - halfStep);
-                               context2.measureNeighborEncode(x, y + halfStep);
-                       
-                               
-                               savePixel(step, offsetX, offsetY, x, y,
-                                               context2.colorStats.getAverageY(),
-                                               context2.colorStats.getAverageU(),
-                                               context2.colorStats.getAverageV());                             
+               saveGridDiagonal(step / 2, step / 2, step);
+               saveGridSquare(step / 2, 0, step);
+               saveGridSquare(0, step / 2, step);
 
-                       }                       
-               }               
+               if (step > 2)
+                       saveGrid(step / 2);
        }
 
-       public void saveGridDiagonal(int offsetX, int offsetY, int step) throws IOException {
-               for (int y = offsetY; y < height; y = y + step){
-                       for (int x = offsetX; x < width; x = x + step){
-                               
-                               int halfStep = step / 2;
+       public void saveGridDiagonal(final int offsetX, final int offsetY,
+                       final int step) throws IOException {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-                               context2.initialize(image, yChannel.decodedMap, uChannel.decodedMap,  vChannel.decodedMap);
+                               final int halfStep = step / 2;
+
+                               context2.initialize(image, yChannel.decodedMap,
+                                               uChannel.decodedMap, vChannel.decodedMap);
                                context2.measureNeighborEncode(x - halfStep, y - halfStep);
                                context2.measureNeighborEncode(x + halfStep, y - halfStep);
                                context2.measureNeighborEncode(x - halfStep, y + halfStep);
                                context2.measureNeighborEncode(x + halfStep, y + halfStep);
-                       
-                               
+
                                savePixel(step, offsetX, offsetY, x, y,
                                                context2.colorStats.getAverageY(),
                                                context2.colorStats.getAverageU(),
-                                               context2.colorStats.getAverageV());                             
-                               
-                       }                       
-               }               
+                                               context2.colorStats.getAverageV());
+
+                       }
+               }
        }
 
-       public void savePixel(int step, int offsetX, int offsetY, int x, int y, int averageDecodedY, int averageDecodedU, int averageDecodedV) throws IOException {
+       public void saveGridSquare(final int offsetX, final int offsetY,
+                       final int step) throws IOException {
+               for (int y = offsetY; y < height; y = y + step) {
+                       for (int x = offsetX; x < width; x = x + step) {
 
-               int index = (y * width) + x;
+                               final int halfStep = step / 2;
 
-               int py = byteToInt(yChannel.map[index]);                
-               int pu = byteToInt(uChannel.map[index]);        
-               int pv = byteToInt(vChannel.map[index]);                
-               
-               int yRange = byteToInt(yChannel.rangeMap[index]);
-               int uRange = byteToInt(uChannel.rangeMap[index]);
-               int vRange = byteToInt(vChannel.rangeMap[index]);
+                               context2.initialize(image, yChannel.decodedMap,
+                                               uChannel.decodedMap, vChannel.decodedMap);
+                               context2.measureNeighborEncode(x - halfStep, y);
+                               context2.measureNeighborEncode(x + halfStep, y);
+                               context2.measureNeighborEncode(x, y - halfStep);
+                               context2.measureNeighborEncode(x, y + halfStep);
 
-               int halfStep = step / 2;
+                               savePixel(step, offsetX, offsetY, x, y,
+                                               context2.colorStats.getAverageY(),
+                                               context2.colorStats.getAverageU(),
+                                               context2.colorStats.getAverageV());
+
+                       }
+               }
+       }
+
+       public void savePixel(final int step, final int offsetX, final int offsetY,
+                       final int x, final int y, final int averageDecodedY,
+                       final int averageDecodedU, final int averageDecodedV)
+                       throws IOException {
+
+               final int index = (y * width) + x;
+
+               final int py = byteToInt(yChannel.map[index]);
+               final int pu = byteToInt(uChannel.map[index]);
+               final int pv = byteToInt(vChannel.map[index]);
+
+               final int yRange = byteToInt(yChannel.rangeMap[index]);
+               final int uRange = byteToInt(uChannel.rangeMap[index]);
+               final int vRange = byteToInt(vChannel.rangeMap[index]);
+
+               final int halfStep = step / 2;
 
                int parentIndex;
-               if (offsetX > 0){
-                       if (offsetY > 0){
+               if (offsetX > 0) {
+                       if (offsetY > 0) {
                                // diagonal approach
-                               parentIndex = ((y - halfStep) * width) + (x - halfStep);                                                                                                        
+                               parentIndex = ((y - halfStep) * width) + (x - halfStep);
                        } else {
                                // take left pixel
-                               parentIndex = (y * width) + (x - halfStep);                                                                     
-                       }                       
+                               parentIndex = (y * width) + (x - halfStep);
+                       }
                } else {
                        // take upper pixel
-                       parentIndex = ((y - halfStep) * width) + x;                                                                             
+                       parentIndex = ((y - halfStep) * width) + x;
                }
 
-               encodeChannel(
-                               approximator.yTable,
-                               yChannel,
-                               averageDecodedY,
-                               index,
-                               py,
-                               yRange,
-                               parentIndex);
-
-               encodeChannel(
-                               approximator.uTable,
-                               uChannel,
-                               averageDecodedU,
-                               index,
-                               pu,
-                               uRange,
-                               parentIndex);
-
-               encodeChannel(
-                               approximator.vTable,
-                               vChannel,
-                               averageDecodedV,
-                               index,
-                               pv,
-                               vRange,
-                               parentIndex);
+               encodeChannel(approximator.yTable, yChannel, averageDecodedY, index,
+                               py, yRange, parentIndex);
+
+               encodeChannel(approximator.uTable, uChannel, averageDecodedU, index,
+                               pu, uRange, parentIndex);
+
+               encodeChannel(approximator.vTable, vChannel, averageDecodedV, index,
+                               pv, vRange, parentIndex);
 
        }
 
+       public static int byteToInt(final byte input) {
+               int result = input;
+               if (result < 0)
+                       result = result + 256;
+               return result;
+       }
 
-       private void encodeChannel(Table table, Channel channel, int averageDecodedValue, int index,
-                       int value, int range, int parentIndex)
-                       throws IOException {
-               
-               byte[] decodedRangeMap = channel.decodedRangeMap;
-               byte[] decodedMap = channel.decodedMap;
-               
-               int inheritedRange = byteToInt(decodedRangeMap[parentIndex]);
+       public static int decodeValueFromGivenBits(final int encodedBits,
+                       final int range, final int bitCount) {
+               final int negativeBit = encodedBits & 1;
 
-               int inheritedBitCount = table.proposeBitcountForRange(inheritedRange);
+               final int remainingBitCount = bitCount - 1;
 
-               if (inheritedBitCount > 0){
-                       int computedRange;
-                       computedRange = table.proposeRangeForRange(range, inheritedRange);                                                                                                                              
-                       decodedRangeMap[index] = (byte)computedRange;
+               if (remainingBitCount == 0) {
+                       // no more bits remaining to encode actual value
 
-                       channel.bitCount++;
-                       if (computedRange != inheritedRange){
-                               // brightness range shrinked
-                               bitOutputStream.storeBits(1, 1);                        
+                       if (negativeBit == 0) {
+                               return range;
                        } else {
-                               // brightness range stayed the same
-                               bitOutputStream.storeBits(0, 1);                                                
+                               return -range;
                        }
 
+               } else {
+                       // still one or more bits left, encode value as precisely as
+                       // possible
 
-                       // encode brightness into available amount of bits
-                       int computedBitCount = table.proposeBitcountForRange(computedRange);
-
-                       if (computedBitCount > 0){
+                       final int encodedValue = (encodedBits >>> 1) + 1;
 
-                               int differenceToEncode = -(value - averageDecodedValue);
-                               int bitEncodedDifference = encodeValueIntoGivenBits(differenceToEncode, computedRange, computedBitCount);
+                       final int realvalueForThisBitcount = 1 << remainingBitCount;
 
-                               channel.bitCount = channel.bitCount + computedBitCount;
-                               bitOutputStream.storeBits(bitEncodedDifference, computedBitCount);                                                              
+                       // int valueMultiplier = range / realvalueForThisBitcount;
+                       int decodedValue = (range * encodedValue)
+                                       / realvalueForThisBitcount;
 
-                               int decodedDifference = decodeValueFromGivenBits(bitEncodedDifference, computedRange, computedBitCount);
-                               int decodedValue = averageDecodedValue - decodedDifference;
-                               if (decodedValue > 255) decodedValue = 255;
-                               if (decodedValue < 0) decodedValue = 0;
+                       if (decodedValue > range)
+                               decodedValue = range;
 
-                               decodedMap[index] = (byte)decodedValue;                 
-                       } else {                                
-                               decodedMap[index] = (byte)averageDecodedValue;                  
-                       }                       
+                       if (negativeBit == 0) {
+                               return decodedValue;
+                       } else {
+                               return -decodedValue;
+                       }
 
-               } else {
-                       decodedRangeMap[index] = (byte)inheritedRange;                  
-                       decodedMap[index] = (byte)averageDecodedValue;                  
                }
        }
 
-       public static int encodeValueIntoGivenBits(int value, int range, int bitCount){
+       public static int encodeValueIntoGivenBits(int value, final int range,
+                       final int bitCount) {
 
                int negativeBit = 0;
 
-               if (value <0){
+               if (value < 0) {
                        negativeBit = 1;
                        value = -value;
                }
 
-               int remainingBitCount = bitCount - 1;
+               final int remainingBitCount = bitCount - 1;
 
-               if (remainingBitCount == 0){                    
+               if (remainingBitCount == 0) {
                        // no more bits remaining to encode actual value
 
                        return negativeBit;
 
                } else {
-                       // still one or more bits left, encode value as precisely as possible
-
-                       if (value > range) value = range;
+                       // still one or more bits left, encode value as precisely as
+                       // possible
 
+                       if (value > range)
+                               value = range;
 
-                       int realvalueForThisBitcount = 1 << remainingBitCount;
+                       final int realvalueForThisBitcount = 1 << remainingBitCount;
                        // int valueMultiplier = range / realvalueForThisBitcount;
-                       int encodedValue = value * realvalueForThisBitcount / range;
+                       int encodedValue = (value * realvalueForThisBitcount) / range;
 
-                       if (encodedValue >= realvalueForThisBitcount) encodedValue = realvalueForThisBitcount - 1;
+                       if (encodedValue >= realvalueForThisBitcount)
+                               encodedValue = realvalueForThisBitcount - 1;
 
                        encodedValue = (encodedValue << 1) + negativeBit;
 
@@ -483,47 +545,4 @@ public class ImageEncoder {
                }
        }
 
-
-       public static int decodeValueFromGivenBits(int encodedBits, int range, int bitCount){
-               int negativeBit = encodedBits & 1;
-
-               int remainingBitCount = bitCount - 1;
-
-               if (remainingBitCount == 0){                    
-                       // no more bits remaining to encode actual value
-
-                       if (negativeBit == 0){
-                               return range;                           
-                       } else {
-                               return -range;                                                          
-                       }
-
-               } else {
-                       // still one or more bits left, encode value as precisely as possible
-
-                       int encodedValue = (encodedBits >>> 1) + 1;
-
-                       int realvalueForThisBitcount = 1 << remainingBitCount;
-
-                       // int valueMultiplier = range / realvalueForThisBitcount;
-                       int decodedValue = range * encodedValue / realvalueForThisBitcount;
-
-
-                       if (decodedValue > range) decodedValue = range;
-
-                       if (negativeBit == 0){
-                               return decodedValue;                            
-                       } else {
-                               return -decodedValue;                                                           
-                       }
-
-               }               
-       }
-
-       public static int byteToInt(byte input){
-               int result = input;
-               if (result < 0) result = result + 256;
-               return result;
-       }
-
 }