/*
* 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.
public class ImageDecoder {
+ public static int readIntegerCompressed8(final BitInputStream inputStream)
+ throws IOException {
+
+ if (inputStream.readBits(1) == 0)
+ return inputStream.readBits(8);
+ else
+ return inputStream.readBits(32);
+ }
+
int width, height;
- Image image;
+ Image image;
byte[] decodedYRangeMap;
- byte[] decodedYMap;
+ byte[] decodedYMap;
byte[] decodedURangeMap;
- byte[] decodedUMap;
+ byte[] decodedUMap;
byte[] decodedVRangeMap;
+
byte[] decodedVMap;
Color tmpColor = new Color();
-
Approximator approximator;
- BitInputStream bitInputStream;
+ BitInputStream bitInputStream;
ColorStats colorStats = new ColorStats();
+
OperatingContext context = new OperatingContext();
public ImageDecoder(final Image image, final BitInputStream bitInputStream) {
// 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;
- }
grid(initialStep, pixels);
}
gridSquare(step / 2, 0, step, pixels);
gridSquare(0, step / 2, step, pixels);
- if (step > 2) {
+ if (step > 2)
grid(step / 2, pixels);
- }
}
public void gridDiagonal(final int offsetX, final int offsetY,
final int step, final byte[] pixels) throws IOException {
- for (int y = offsetY; y < height; y = y + step) {
+ for (int y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int halfStep = step / 2;
context.colorStats.getAverageV());
}
- }
}
public void gridSquare(final int offsetX, final int offsetY,
final int step, final byte[] pixels) throws IOException {
- for (int y = offsetY; y < height; y = y + step) {
+ for (int y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int halfStep = step / 2;
context.colorStats.getAverageV());
}
- }
}
private int loadChannel(final byte[] decodedRangeMap,
if (bitCount > 0) {
final int rangeDecreases = bitInputStream.readBits(1);
- if (rangeDecreases != 0) {
+ if (rangeDecreases != 0)
computedRange = table.proposeDecreasedRange(inheritedRange);
- }
decodedRangeMap[index] = (byte) computedRange;
computedRangeBitCount = table
computedRange, computedRangeBitCount);
decodedValue = averageDecodedValue - decodedDifference;
- if (decodedValue > 255) {
+ if (decodedValue > 255)
decodedValue = 255;
- }
- if (decodedValue < 0) {
+ if (decodedValue < 0)
decodedValue = 0;
- }
}
- } else {
+ } else
decodedRangeMap[index] = (byte) inheritedRange;
- }
decodedMap[index] = (byte) decodedValue;
return decodedValue;
}
int parentIndex;
if (offsetX > 0) {
- if (offsetY > 0) {
+ if (offsetY > 0)
// diagonal approach
parentIndex = ((y - halfStep) * width) + (x - halfStep);
- } else {
+ else
// take left pixel
parentIndex = (y * width) + (x - halfStep);
- }
- } else {
+ } else
// take upper pixel
parentIndex = ((y - halfStep) * width) + x;
- }
final int colorBufferIndex = index * 3;
/*
* 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.
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();
OperatingContext context = new OperatingContext();
+
OperatingContext context2 = new OperatingContext();
BitOutputStream bitOutputStream;
final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
final byte[] pixels = dbi.getData();
- if (yChannel == null) {
+ if (yChannel == null)
yChannel = new Channel(width, height);
- } else {
+ else
yChannel.reset();
- }
- if (uChannel == null) {
+ if (uChannel == null)
uChannel = new Channel(width, height);
- } else {
+ else
uChannel.reset();
- }
- if (vChannel == null) {
+ if (vChannel == null)
vChannel = new Channel(width, height);
- } else {
+ else
vChannel.reset();
- }
// create YUV map out of RGB raster data
final Color color = new Color();
- for (int y = 0; y < height; y++) {
+ for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++) {
final int index = (y * width) + x;
final int colorBufferIndex = index * 3;
int blue = pixels[colorBufferIndex];
- if (blue < 0) {
+ if (blue < 0)
blue = blue + 256;
- }
int green = pixels[colorBufferIndex + 1];
- if (green < 0) {
+ if (green < 0)
green = green + 256;
- }
int red = pixels[colorBufferIndex + 2];
- if (red < 0) {
+ if (red < 0)
red = red + 256;
- }
color.r = red;
color.g = green;
uChannel.map[index] = (byte) color.u;
vChannel.map[index] = (byte) color.v;
}
- }
yChannel.decodedMap[0] = yChannel.map[0];
uChannel.decodedMap[0] = uChannel.map[0];
// 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);
decodedRangeMap[index] = (byte) computedRange;
channel.bitCount++;
- if (computedRange != inheritedRange) {
+ if (computedRange != inheritedRange)
// brightness range shrinked
bitOutputStream.storeBits(1, 1);
- } else {
+ else
// brightness range stayed the same
bitOutputStream.storeBits(0, 1);
- }
// encode brightness into available amount of bits
final int computedBitCount = table
final int decodedDifference = decodeValueFromGivenBits(
bitEncodedDifference, computedRange, computedBitCount);
int decodedValue = averageDecodedValue - decodedDifference;
- if (decodedValue > 255) {
+ if (decodedValue > 255)
decodedValue = 255;
- }
- if (decodedValue < 0) {
+ if (decodedValue < 0)
decodedValue = 0;
- }
decodedMap[index] = (byte) decodedValue;
- } else {
+ } else
decodedMap[index] = (byte) averageDecodedValue;
- }
} else {
decodedRangeMap[index] = (byte) inheritedRange;
rangeGridSquare(step / 2, 0, step);
rangeGridSquare(0, step / 2, step);
- if (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 y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int index = (y * width) + x;
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 y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int index = (y * width) + x;
uChannel.rangeMap[index] = (byte) context.getURange(index);
vChannel.rangeMap[index] = (byte) context.getVRange(index);
}
- }
}
public void rangeRoundGrid(final int step) {
rangeRoundGridSquare(step / 2, 0, step);
rangeRoundGridSquare(0, step / 2, step);
- if (step < 1024) {
+ if (step < 1024)
rangeRoundGrid(step * 2);
- }
}
public void rangeRoundGridDiagonal(final int offsetX, final int offsetY,
final int step) {
- for (int y = offsetY; y < height; y = y + 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;
vChannel.rangeMap[parentIndex] = (byte) parentVRange;
}
}
- }
}
public void rangeRoundGridSquare(final int offsetX, final int offsetY,
final int step) {
- for (int y = offsetY; y < height; y = y + 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 parentIndex;
- if (offsetX > 0) {
+ if (offsetX > 0)
parentIndex = (y * width) + (x - halfStep);
- } else {
+ else
parentIndex = ((y - halfStep) * width) + x;
- }
int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
}
}
- }
}
public void saveGrid(final int step) throws IOException {
saveGridSquare(step / 2, 0, step);
saveGridSquare(0, step / 2, step);
- if (step > 2) {
+ if (step > 2)
saveGrid(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 y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int halfStep = step / 2;
context2.colorStats.getAverageV());
}
- }
}
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 y = offsetY; y < height; y = y + step)
for (int x = offsetX; x < width; x = x + step) {
final int halfStep = step / 2;
context2.colorStats.getAverageV());
}
- }
}
public void savePixel(final int step, final int offsetX, final int offsetY,
int parentIndex;
if (offsetX > 0) {
- if (offsetY > 0) {
+ if (offsetY > 0)
// diagonal approach
parentIndex = ((y - halfStep) * width) + (x - halfStep);
- } else {
+ else
// take left pixel
parentIndex = (y * width) + (x - halfStep);
- }
- } else {
+ } else
// take upper pixel
parentIndex = ((y - halfStep) * width) + x;
- }
encodeChannel(approximator.yTable, yChannel, averageDecodedY, index,
py, yRange, parentIndex);
}
- 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) {
- // 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;
- }
-
- 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;
- }
- }
-
}
/*
* 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.
for (int i = 0; i < 256; i++) {
- if (range[currentCheckPointer] == i) {
+ if (range[currentCheckPointer] == i)
currentCheckPointer++;
- }
- if (currentCheckPointer > 0) {
+ if (currentCheckPointer > 0)
bitCountForRange[i] = bitcount[currentCheckPointer - 1];
- } else {
+ else
bitCountForRange[i] = 0;
- }
}
int seek;
seekLoop: {
- for (seek = 0; seek < usedEntries; seek++) {
-
+ for (seek = 0; seek < usedEntries; seek++)
if (switchTreshold[seek] >= i)
break seekLoop;
-
- }
}
proposedRangeForActualRange[i] = range[seek];
- if (seek == 0) {
+ if (seek == 0)
proposedRangeForActualRangeLow[i] = 0;
- } else {
+ else
proposedRangeForActualRangeLow[i] = switchTreshold[seek - 1] + 1;
- }
proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
}
if (range[currentCheckPointer] == i)
currentCheckPointer--;
- if (currentCheckPointer < 0) {
+ if (currentCheckPointer < 0)
proposedDecreasedRange[i] = 0;
- } else {
+ else
proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
- }
}
}
public void load(final BitInputStream inputStream) throws IOException {
reset();
- final int availableEntries = inputStream.readIntegerCompressed8();
+ final int availableEntries = ImageDecoder
+ .readIntegerCompressed8(inputStream);
- for (int i = 0; i < availableEntries; i++) {
+ for (int i = 0; i < availableEntries; i++)
addEntry(inputStream.readBits(8), inputStream.readBits(8),
inputStream.readBits(4));
- }
}
public int proposeBitcountForRange(int range) {
if (inheritedRange < 0)
inheritedRange = 0;
- if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange) {
+ if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
return inheritedRange;
- }
return proposeDecreasedRange(inheritedRange);
}
}
public void save(final BitOutputStream outputStream) throws IOException {
- outputStream.storeIntegerCompressed8(usedEntries);
+ ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
for (int i = 0; i < usedEntries; i++) {
outputStream.storeBits(range[i], 8);