From a4acb4f31760b0c654f9a041eef9797782d6043a Mon Sep 17 00:00:00 2001 From: Svjatoslav Agejenko Date: Fri, 20 Mar 2015 23:26:09 +0200 Subject: [PATCH] moved compressed integer support out of svjatoslavcommons --- pom.xml | 2 +- .../imagesqueeze/codec/ImageDecoder.java | 59 ++-- .../imagesqueeze/codec/ImageEncoder.java | 273 ++++++++---------- .../imagesqueeze/codec/ImageMetaData.java | 16 +- .../svjatoslav/imagesqueeze/codec/Table.java | 36 +-- 5 files changed, 177 insertions(+), 209 deletions(-) diff --git a/pom.xml b/pom.xml index f261444..8655494 100644 --- a/pom.xml +++ b/pom.xml @@ -98,7 +98,7 @@ eu.svjatoslav svjatoslavcommons - 1.0-SNAPSHOT + 1.5-SNAPSHOT diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java index 78bf715..53f2d91 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java @@ -1,7 +1,7 @@ /* * 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. @@ -21,24 +21,34 @@ import eu.svjatoslav.commons.data.BitInputStream; 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) { @@ -91,15 +101,13 @@ public class ImageDecoder { // 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); } @@ -110,15 +118,14 @@ public class ImageDecoder { 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; @@ -135,13 +142,12 @@ public class ImageDecoder { 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; @@ -158,7 +164,6 @@ public class ImageDecoder { context.colorStats.getAverageV()); } - } } private int loadChannel(final byte[] decodedRangeMap, @@ -176,9 +181,8 @@ public class ImageDecoder { 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 @@ -194,16 +198,13 @@ public class ImageDecoder { 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; } @@ -219,17 +220,15 @@ public class ImageDecoder { 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; diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java index 7e8af8d..5b0e8db 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java @@ -1,7 +1,7 @@ /* * 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. @@ -21,21 +21,114 @@ 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(); OperatingContext context = new OperatingContext(); + OperatingContext context2 = new OperatingContext(); BitOutputStream bitOutputStream; @@ -64,47 +157,41 @@ public class ImageEncoder { 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; @@ -116,7 +203,6 @@ public class ImageEncoder { uChannel.map[index] = (byte) color.u; vChannel.map[index] = (byte) color.v; } - } yChannel.decodedMap[0] = yChannel.map[0]; uChannel.decodedMap[0] = uChannel.map[0]; @@ -129,15 +215,13 @@ public class ImageEncoder { // 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); @@ -162,13 +246,12 @@ public class ImageEncoder { 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 @@ -187,17 +270,14 @@ public class ImageEncoder { 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; @@ -224,14 +304,13 @@ public class ImageEncoder { 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; @@ -249,12 +328,11 @@ public class ImageEncoder { 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; @@ -272,7 +350,6 @@ public class ImageEncoder { uChannel.rangeMap[index] = (byte) context.getURange(index); vChannel.rangeMap[index] = (byte) context.getVRange(index); } - } } public void rangeRoundGrid(final int step) { @@ -281,14 +358,13 @@ public class ImageEncoder { 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; @@ -323,12 +399,11 @@ public class ImageEncoder { 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; @@ -340,11 +415,10 @@ public class ImageEncoder { 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]); @@ -368,7 +442,6 @@ public class ImageEncoder { } } - } } public void saveGrid(final int step) throws IOException { @@ -377,14 +450,13 @@ public class ImageEncoder { 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; @@ -402,12 +474,11 @@ public class ImageEncoder { 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; @@ -425,7 +496,6 @@ public class ImageEncoder { context2.colorStats.getAverageV()); } - } } public void savePixel(final int step, final int offsetX, final int offsetY, @@ -447,17 +517,15 @@ public class ImageEncoder { 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); @@ -470,91 +538,4 @@ 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) { - // 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; - } - } - } diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageMetaData.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageMetaData.java index 4b21a22..49e9000 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageMetaData.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageMetaData.java @@ -1,7 +1,7 @@ /* * 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. @@ -11,7 +11,7 @@ package eu.svjatoslav.imagesqueeze.codec; /** * Class to hold image metadata. - * Like image dimensions, header version, compression quality, etc.. + * Like image dimensions, header version, compression quality, etc.. */ import java.io.IOException; @@ -26,19 +26,15 @@ public class ImageMetaData { int height; public void load(final BitInputStream inputStream) throws IOException { - version = inputStream.readBits(16); - width = inputStream.readIntegerCompressed8(); - height = inputStream.readIntegerCompressed8(); - + width = ImageDecoder.readIntegerCompressed8(inputStream); + height = ImageDecoder.readIntegerCompressed8(inputStream); } public void save(final BitOutputStream outputStream) throws IOException { - outputStream.storeBits(version, 16); - outputStream.storeIntegerCompressed8(width); - outputStream.storeIntegerCompressed8(height); - + ImageEncoder.storeIntegerCompressed8(outputStream, width); + ImageEncoder.storeIntegerCompressed8(outputStream, height); } } diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/Table.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/Table.java index 33637d7..4e6e638 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/Table.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/Table.java @@ -1,7 +1,7 @@ /* * 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. @@ -94,15 +94,13 @@ public class Table implements Comparable { 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; - } } @@ -110,20 +108,16 @@ public class Table implements Comparable
{ 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]; } @@ -132,11 +126,10 @@ public class Table implements Comparable
{ if (range[currentCheckPointer] == i) currentCheckPointer--; - if (currentCheckPointer < 0) { + if (currentCheckPointer < 0) proposedDecreasedRange[i] = 0; - } else { + else proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]); - } } } @@ -144,12 +137,12 @@ public class Table implements Comparable
{ 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) { @@ -177,9 +170,8 @@ public class Table implements Comparable
{ if (inheritedRange < 0) inheritedRange = 0; - if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange) { + if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange) return inheritedRange; - } return proposeDecreasedRange(inheritedRange); } @@ -199,7 +191,7 @@ public class Table implements Comparable
{ } 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); -- 2.20.1