-public class ImageEncoder {
-
- Image image;
- int width, height;
-
- Channel yChannel;
- Channel uChannel;
- Channel vChannel;
-
- Approximator approximator;
-
- int bitsForY;
- int bitsForU;
- int bitsForV;
-
- //ColorStats colorStats = new ColorStats();
- OperatingContext context = new OperatingContext();
- OperatingContext context2 = new OperatingContext();
-
- BitOutputStream bitOutputStream;
-
- public ImageEncoder(Image image){
- approximator = new Approximator();
-
- //bitOutputStream = outputStream;
-
- this.image = image;
-
- }
-
-
- public void encode(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();
-
- if (yChannel == null){
- yChannel = new Channel(width, height);
- } else {
- yChannel.reset();
- }
-
- if (uChannel == null){
- uChannel = new Channel(width, height);
- } else {
- uChannel.reset();
- }
-
- 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++){
-
- int index = (y * width) + x;
- int colorBufferIndex = index * 3;
-
- int blue = pixels[colorBufferIndex];
- if (blue < 0) blue = blue + 256;
-
- int green = pixels[colorBufferIndex+1];
- if (green < 0) green = green + 256;
-
- int red = pixels[colorBufferIndex+2];
- if (red < 0) red = red + 256;
-
- color.r = red;
- color.g = green;
- color.b = blue;
-
- color.RGB2YUV();
-
- 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);
-
- // detect initial step
- int largestDimension;
- int initialStep = 2;
- if (width > height) {
- largestDimension = width;
- } else {
- largestDimension = height;
- }
-
- while (initialStep < largestDimension){
- initialStep = initialStep * 2;
- }
-
- rangeGrid(initialStep);
- rangeRoundGrid(2);
- saveGrid(initialStep);
- }
-
- public void printStatistics(){
- System.out.println("Y channel:");
- yChannel.printStatistics();
-
- System.out.println("U channel:");
- uChannel.printStatistics();
-
- System.out.println("V channel:");
- vChannel.printStatistics();
- }
-
- public void rangeGrid(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);
- }
-
-
- 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);
- }
-
- public void saveGrid(int step) throws IOException {
-
- saveGridDiagonal(step / 2, step / 2, step);
- saveGridSquare(step / 2, 0, step);
- saveGridSquare(0, step / 2, step);
-
- if (step > 2) saveGrid(step / 2);
- }
-
-
- 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){
-
- int index = (y * width) + x;
- int halfStep = step / 2;
-
- 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);
- }
- }
- }
-
- 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){
-
- 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);
-
- yChannel.rangeMap[index] = (byte)context.getYRange(index);
- uChannel.rangeMap[index] = (byte)context.getURange(index);
- vChannel.rangeMap[index] = (byte)context.getVRange(index);
- }
- }
- }
-
- 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){
-
- int index = (y * width) + x;
-
- int yRange = byteToInt(yChannel.rangeMap[index]);
- int uRange = byteToInt(uChannel.rangeMap[index]);
- int vRange = byteToInt(vChannel.rangeMap[index]);
-
- int halfStep = step / 2;
-
- int parentIndex = ((y - halfStep) * width) + (x - halfStep);
-
- int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
-
- if (parentYRange < yRange){
- parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte)parentYRange;
- }
-
- int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
-
- if (parentURange < uRange){
- parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte)parentURange;
- }
-
- int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
-
- if (parentVRange < vRange){
- parentVRange = vRange;
- 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){
-
- int index = (y * width) + x;
-
- int yRange = byteToInt(yChannel.rangeMap[index]);
- int uRange = byteToInt(uChannel.rangeMap[index]);
- int vRange = byteToInt(vChannel.rangeMap[index]);
-
- int halfStep = step / 2;
-
- int parentIndex;
- if (offsetX > 0){
- parentIndex = (y * width) + (x - halfStep);
- } else {
- parentIndex = ((y - halfStep) * width) + x;
- }
-
- int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
-
- if (parentYRange < yRange){
- parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte)parentYRange;
- }
-
- int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
-
- if (parentURange < uRange){
- parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte)parentURange;
- }
-
- int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
-
- if (parentVRange < vRange){
- parentVRange = vRange;
- 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;
-
- 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());
-
- }
- }
- }
-
- 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;
-
- 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());
-
- }
- }
- }
-
- public void savePixel(int step, int offsetX, int offsetY, int x, int y, int averageDecodedY, int averageDecodedU, int averageDecodedV) throws IOException {
-
- int index = (y * width) + x;
-
- 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]);
-
- int halfStep = step / 2;
-
- int parentIndex;
- if (offsetX > 0){
- if (offsetY > 0){
- // diagonal approach
- parentIndex = ((y - halfStep) * width) + (x - halfStep);
- } else {
- // take left pixel
- parentIndex = (y * width) + (x - halfStep);
- }
- } else {
- // take upper pixel
- 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);
-
- }
-
-
- 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]);
-
- int inheritedBitCount = table.proposeBitcountForRange(inheritedRange);
-
- if (inheritedBitCount > 0){
- int computedRange;
- computedRange = table.proposeRangeForRange(range, inheritedRange);
- decodedRangeMap[index] = (byte)computedRange;