+/*
+ * 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;
/**
import java.awt.image.WritableRaster;
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitOutputStream;
public class ImageEncoder {
+ public static int byteToInt(final byte input) {
+ int result = input;
+ if (result < 0)
+ result = result + 256;
+ return result;
+ }
+
+ public static int decodeValueFromGivenBits(final int encodedBits,
+ final int range, final int bitCount) {
+ final int negativeBit = encodedBits & 1;
+
+ final 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
+
+ final int encodedValue = (encodedBits >>> 1) + 1;
+
+ final 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 encodeValueIntoGivenBits(int value, final int range,
+ final int bitCount) {
+
+ int negativeBit = 0;
+
+ if (value < 0) {
+ negativeBit = 1;
+ value = -value;
+ }
+
+ final int remainingBitCount = bitCount - 1;
+
+ if (remainingBitCount == 0)
+ return negativeBit;
+ else {
+ // still one or more bits left, encode value as precisely as
+ // possible
+
+ if (value > range)
+ value = range;
+
+ final int realvalueForThisBitcount = 1 << remainingBitCount;
+ // int valueMultiplier = range / realvalueForThisBitcount;
+ int encodedValue = (value * realvalueForThisBitcount) / range;
+
+ if (encodedValue >= realvalueForThisBitcount)
+ encodedValue = realvalueForThisBitcount - 1;
+
+ encodedValue = (encodedValue << 1) + negativeBit;
+
+ return encodedValue;
+ }
+ }
+
+ public static void storeIntegerCompressed8(
+ final BitOutputStream outputStream, final int data)
+ throws IOException {
+
+ if (data < 256) {
+ outputStream.storeBits(0, 1);
+ outputStream.storeBits(data, 8);
+ } else {
+ outputStream.storeBits(1, 1);
+ outputStream.storeBits(data, 32);
+ }
+ }
+
Image image;
int width, height;
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);
- } else {
+ if (yChannel == null)
+ yChannel = new Channel(width, height);
+ else
yChannel.reset();
- }
-
- if (uChannel == null){
- uChannel = new Channel(width, height);
- } else {
+
+ if (uChannel == null)
+ uChannel = new Channel(width, height);
+ else
uChannel.reset();
- }
-
- if (vChannel == null){
- vChannel = new Channel(width, height);
- } else {
+
+ 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();
- int index = (y * width) + x;
- int colorBufferIndex = index * 3;
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++) {
- int blue = pixels[colorBufferIndex];
- if (blue < 0) blue = blue + 256;
+ final int index = (y * width) + x;
+ final int colorBufferIndex = index * 3;
- int green = pixels[colorBufferIndex+1];
- if (green < 0) green = green + 256;
+ int blue = pixels[colorBufferIndex];
+ if (blue < 0)
+ blue = blue + 256;
- int red = pixels[colorBufferIndex+2];
- if (red < 0) red = red + 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.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;
int initialStep = 2;
- if (width > height) {
+ if (width > height)
largestDimension = width;
- } else {
+ 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){
+ final int index = (y * width) + x;
+ final int halfStep = step / 2;
- rangeRoundGridDiagonal(step / 2, step / 2, step);
- rangeRoundGridSquare(step / 2, 0, step);
- rangeRoundGridSquare(0, step / 2, step);
-
- if (step < 1024) rangeRoundGrid(step * 2);
- }
+ context.initialize(image, yChannel.map, uChannel.map,
+ vChannel.map);
- public void saveGrid(int step) throws IOException {
-
- 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) {
+
+ final int index = (y * width) + x;
- 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;
- if (offsetX > 0){
- parentIndex = (y * width) + (x - halfStep);
- } else {
- parentIndex = ((y - halfStep) * width) + x;
- }
+ if (offsetX > 0)
+ parentIndex = (y * width) + (x - halfStep);
+ else
+ 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){
+ public void saveGrid(final int step) throws IOException {
- 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());
+ 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) {
+
+ final int halfStep = step / 2;
- context2.initialize(image, yChannel.decodedMap, uChannel.decodedMap, vChannel.decodedMap);
+ 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);
+ context2.colorStats.getAverageV());
- }
-
-
- 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;
-
- 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
- int computedBitCount = table.proposeBitcountForRange(computedRange);
-
- if (computedBitCount > 0){
-
- int differenceToEncode = -(value - averageDecodedValue);
- int bitEncodedDifference = encodeValueIntoGivenBits(differenceToEncode, computedRange, computedBitCount);
-
- channel.bitCount = channel.bitCount + computedBitCount;
- bitOutputStream.storeBits(bitEncodedDifference, computedBitCount);
-
- 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 static int encodeValueIntoGivenBits(int value, int range, int bitCount){
-
- int negativeBit = 0;
-
- if (value <0){
- negativeBit = 1;
- value = -value;
- }
-
- int remainingBitCount = bitCount - 1;
-
- 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;
+ 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) {
+ final int halfStep = step / 2;
- int realvalueForThisBitcount = 1 << remainingBitCount;
- // int valueMultiplier = range / realvalueForThisBitcount;
- int encodedValue = value * realvalueForThisBitcount / range;
-
- if (encodedValue >= realvalueForThisBitcount) encodedValue = realvalueForThisBitcount - 1;
+ 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);
- encodedValue = (encodedValue << 1) + negativeBit;
+ savePixel(step, offsetX, offsetY, x, y,
+ context2.colorStats.getAverageY(),
+ context2.colorStats.getAverageU(),
+ context2.colorStats.getAverageV());
- return encodedValue;
- }
+ }
}
+ 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 {
- 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;
- }
+ final int index = (y * width) + x;
- } else {
- // still one or more bits left, encode value as precisely as possible
+ final int py = byteToInt(yChannel.map[index]);
+ final int pu = byteToInt(uChannel.map[index]);
+ final int pv = byteToInt(vChannel.map[index]);
- int encodedValue = (encodedBits >>> 1) + 1;
+ final int yRange = byteToInt(yChannel.rangeMap[index]);
+ final int uRange = byteToInt(uChannel.rangeMap[index]);
+ final int vRange = byteToInt(vChannel.rangeMap[index]);
- int realvalueForThisBitcount = 1 << remainingBitCount;
-
- // int valueMultiplier = range / realvalueForThisBitcount;
- int decodedValue = range * encodedValue / realvalueForThisBitcount;
+ final 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;
- if (decodedValue > range) decodedValue = range;
+ encodeChannel(approximator.yTable, yChannel, averageDecodedY, index,
+ py, yRange, parentIndex);
- if (negativeBit == 0){
- return decodedValue;
- } else {
- return -decodedValue;
- }
+ encodeChannel(approximator.uTable, uChannel, averageDecodedU, index,
+ pu, uRange, parentIndex);
- }
- }
+ encodeChannel(approximator.vTable, vChannel, averageDecodedV, index,
+ pv, vRange, parentIndex);
- public static int byteToInt(byte input){
- int result = input;
- if (result < 0) result = result + 256;
- return result;
}
}