X-Git-Url: http://www2.svjatoslav.eu/gitweb/?p=imagesqueeze.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Feu%2Fsvjatoslav%2Fimagesqueeze%2Fcodec%2FImageDecoder.java;h=a13105944744ca4b1c0bb763323539d920cb1ce4;hp=37f3c0e4c6ac0f37081717fd485038f7ada83752;hb=HEAD;hpb=c7d0b8e1723045c0df086d9214a35f54db47684c diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java index 37f3c0e..a131059 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java @@ -1,232 +1,238 @@ +/* + * Image codec. Author: Svjatoslav Agejenko, svjatoslav@svjatoslav.eu + * This project is released under Creative Commons Zero (CC0) license. + */ package eu.svjatoslav.imagesqueeze.codec; /** * Compressed image pixels decoder. */ +import eu.svjatoslav.commons.data.BitInputStream; + import java.awt.image.DataBufferByte; import java.awt.image.WritableRaster; import java.io.IOException; -public class ImageDecoder { - - int width, height; - Image image; +class ImageDecoder { + + private final int width; + private final int height; + private final Image image; + private final byte[] decodedYRangeMap; + private final byte[] decodedYMap; + private final byte[] decodedURangeMap; + private final byte[] decodedUMap; + private final byte[] decodedVRangeMap; + private final byte[] decodedVMap; + private final Approximator approximator; + private final BitInputStream bitInputStream; + private final OperatingContext context = new OperatingContext(); + + public ImageDecoder(final Image image, final BitInputStream bitInputStream) { + approximator = new Approximator(); + + this.image = image; + this.bitInputStream = bitInputStream; + + width = image.metaData.width; + height = image.metaData.height; + + decodedYRangeMap = new byte[width * height]; + decodedYRangeMap[0] = (byte) (255); + decodedYMap = new byte[width * height]; + + decodedURangeMap = new byte[width * height]; + decodedURangeMap[0] = (byte) (255); + decodedUMap = new byte[width * height]; + + decodedVRangeMap = new byte[width * height]; + decodedVRangeMap[0] = (byte) (255); + decodedVMap = new byte[width * height]; - byte [] decodedYRangeMap; - byte [] decodedYMap; - - byte [] decodedURangeMap; - byte [] decodedUMap; - - byte [] decodedVRangeMap; - byte [] decodedVMap; - - Color tmpColor = new Color(); - - Approximator approximator; - BitInputStream bitInputStream; - - ColorStats colorStats = new ColorStats(); - OperatingContext context = new OperatingContext(); - - public ImageDecoder (Image image, BitInputStream bitInputStream) { - approximator = new Approximator(); - - this.image = image; - this.bitInputStream = bitInputStream; - - width = image.metaData.width; - height = image.metaData.height; - - decodedYRangeMap = new byte[width * height]; - decodedYRangeMap[0] = (byte)(255); - decodedYMap = new byte[width * height]; - - decodedURangeMap = new byte[width * height]; - decodedURangeMap[0] = (byte)(255); - decodedUMap = new byte[width * height]; - - decodedVRangeMap = new byte[width * height]; - decodedVRangeMap[0] = (byte)(255); - decodedVMap = new byte[width * height]; - - } - - - public void decode() throws IOException { - approximator.load(bitInputStream); - approximator.computeLookupTables(); - - WritableRaster raster = image.bufferedImage.getRaster(); - DataBufferByte dbi = (DataBufferByte)raster.getDataBuffer(); - byte [] pixels = dbi.getData(); + } - // load top-, left-most pixel. - decodedYMap[0] = (byte)bitInputStream.readBits(8); - decodedUMap[0] = (byte)bitInputStream.readBits(8); - decodedVMap[0] = (byte)bitInputStream.readBits(8); - - Color color = new Color(); - color.y = ImageEncoder.byteToInt(decodedYMap[0]); - color.u = ImageEncoder.byteToInt(decodedUMap[0]); - color.v = ImageEncoder.byteToInt(decodedVMap[0]); - - color.YUV2RGB(); - - pixels[0] = (byte)color.r; - pixels[0+1] = (byte)color.g; - pixels[0+2] = (byte)color.b; - - - // detect initial step - int largestDimension; - int initialStep = 2; - if (width > height) { - largestDimension = width; - } else { - largestDimension = height; - } - - while (initialStep < largestDimension){ - initialStep = initialStep * 2; - } - - grid(initialStep, pixels); - } - - - public void grid(int step, byte [] pixels) throws IOException { - - gridDiagonal(step / 2, step / 2, step, pixels); - gridSquare(step / 2, 0, step, pixels); - gridSquare(0, step / 2, step, pixels); - - if (step > 2) grid(step / 2, pixels); - } - - - public void gridSquare(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{ - - for (int y = offsetY; y < height; y = y + step){ - for (int x = offsetX; x < width; x = x + step){ - - - int halfStep = step / 2; - - context.initialize(image, decodedYMap, decodedUMap, decodedVMap); - context.measureNeighborEncode(x - halfStep, y); - context.measureNeighborEncode(x + halfStep, y); - context.measureNeighborEncode(x, y - halfStep); - context.measureNeighborEncode(x, y + halfStep); - - loadPixel(step, offsetX, offsetY, x, y, pixels, - context.colorStats.getAverageY(), - context.colorStats.getAverageU(), - context.colorStats.getAverageV()); - - } - } - } - - - public void gridDiagonal(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{ - - for (int y = offsetY; y < height; y = y + step){ - for (int x = offsetX; x < width; x = x + step){ - - int halfStep = step / 2; - - context.initialize(image, decodedYMap, decodedUMap, decodedVMap); - context.measureNeighborEncode(x - halfStep, y - halfStep); - context.measureNeighborEncode(x + halfStep, y - halfStep); - context.measureNeighborEncode(x - halfStep, y + halfStep); - context.measureNeighborEncode(x + halfStep, y + halfStep); - - loadPixel(step, offsetX, offsetY, x, y, pixels, - context.colorStats.getAverageY(), - context.colorStats.getAverageU(), - context.colorStats.getAverageV()); - - } - } - } - - - public void loadPixel(int step, int offsetX, int offsetY, int x, int y, byte[] pixels, - int averageDecodedY, int averageDecodedU, int averageDecodedV) - throws IOException{ - - int index = (y * width) + x; - - 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; - } + public static int readIntegerCompressed8(final BitInputStream inputStream) + throws IOException { + if (inputStream.readBits(1) == 0) + return inputStream.readBits(8); + else + return inputStream.readBits(32); + } + public void decode() throws IOException { + approximator.load(bitInputStream); + approximator.computeLookupTables(); - int colorBufferIndex = index * 3; + final WritableRaster raster = image.bufferedImage.getRaster(); + final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); + final byte[] pixels = dbi.getData(); - Color color = new Color(); - color.y = loadChannel(decodedYRangeMap, decodedYMap, approximator.yTable, averageDecodedY, index, parentIndex); - color.u = loadChannel(decodedURangeMap, decodedUMap, approximator.uTable, averageDecodedU, index, parentIndex); - color.v = loadChannel(decodedVRangeMap, decodedVMap, approximator.vTable, averageDecodedV, index, parentIndex); - - color.YUV2RGB(); - - pixels[colorBufferIndex] = (byte)color.r; - pixels[colorBufferIndex+1] = (byte)color.g; - pixels[colorBufferIndex+2] = (byte)color.b; + // load top-, left-most pixel. + decodedYMap[0] = (byte) bitInputStream.readBits(8); + decodedUMap[0] = (byte) bitInputStream.readBits(8); + decodedVMap[0] = (byte) bitInputStream.readBits(8); - } + final Color color = new Color(); + color.y = ImageEncoder.byteToInt(decodedYMap[0]); + color.u = ImageEncoder.byteToInt(decodedUMap[0]); + color.v = ImageEncoder.byteToInt(decodedVMap[0]); + color.YUV2RGB(); - private int loadChannel(byte [] decodedRangeMap, byte [] decodedMap, Table table, int averageDecodedValue, int index, - int parentIndex) throws IOException { - int decodedValue = averageDecodedValue; + pixels[0] = (byte) color.r; + pixels[0 + 1] = (byte) color.g; + pixels[0 + 2] = (byte) color.b; - int inheritedRange = ImageEncoder.byteToInt(decodedRangeMap[parentIndex]); - int computedRange = inheritedRange; + // detect initial step + int largestDimension; + int initialStep = 2; + if (width > height) + largestDimension = width; + else + largestDimension = height; + + while (initialStep < largestDimension) + initialStep = initialStep * 2; - int bitCount = table.proposeBitcountForRange(inheritedRange); - int computedRangeBitCount = 0; - if ( bitCount > 0){ + grid(initialStep, pixels); + } + + private void grid(final int step, final byte[] pixels) throws IOException { + + gridDiagonal(step / 2, step / 2, step, pixels); + gridSquare(step / 2, 0, step, pixels); + gridSquare(0, step / 2, step, pixels); - int rangeDecreases = bitInputStream.readBits(1); - if (rangeDecreases != 0){ - computedRange = table.proposeDecreasedRange(inheritedRange); - } - - decodedRangeMap[index] = (byte)computedRange; - computedRangeBitCount = table.proposeBitcountForRange(computedRange); - - if (computedRangeBitCount > 0){ - - int encodedDifference = bitInputStream.readBits(computedRangeBitCount); - - int decodedDifference = ImageEncoder.decodeValueFromGivenBits(encodedDifference, computedRange, computedRangeBitCount); + if (step > 2) + grid(step / 2, pixels); + } + + private 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 x = offsetX; x < width; x = x + step) { + + final int halfStep = step / 2; + + context.initialize(image, decodedYMap, decodedUMap, decodedVMap); + context.measureNeighborEncode(x - halfStep, y - halfStep); + context.measureNeighborEncode(x + halfStep, y - halfStep); + context.measureNeighborEncode(x - halfStep, y + halfStep); + context.measureNeighborEncode(x + halfStep, y + halfStep); + + loadPixel(step, offsetX, offsetY, x, y, pixels, + context.colorStats.getAverageY(), + context.colorStats.getAverageU(), + context.colorStats.getAverageV()); + + } + } + + private 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 x = offsetX; x < width; x = x + step) { + + final int halfStep = step / 2; + + context.initialize(image, decodedYMap, decodedUMap, decodedVMap); + context.measureNeighborEncode(x - halfStep, y); + context.measureNeighborEncode(x + halfStep, y); + context.measureNeighborEncode(x, y - halfStep); + context.measureNeighborEncode(x, y + halfStep); + + loadPixel(step, offsetX, offsetY, x, y, pixels, + context.colorStats.getAverageY(), + context.colorStats.getAverageU(), + context.colorStats.getAverageV()); + + } + } + + private int loadChannel(final byte[] decodedRangeMap, + final byte[] decodedMap, final Table table, + final int averageDecodedValue, final int index, + final int parentIndex) throws IOException { + int decodedValue = averageDecodedValue; + + final int inheritedRange = ImageEncoder + .byteToInt(decodedRangeMap[parentIndex]); + int computedRange = inheritedRange; + + final int bitCount = table.proposeBitcountForRange(inheritedRange); + int computedRangeBitCount; + if (bitCount > 0) { + + final int rangeDecreases = bitInputStream.readBits(1); + if (rangeDecreases != 0) + computedRange = table.proposeDecreasedRange(inheritedRange); + + decodedRangeMap[index] = (byte) computedRange; + computedRangeBitCount = table + .proposeBitcountForRange(computedRange); + + if (computedRangeBitCount > 0) { + + final int encodedDifference = bitInputStream + .readBits(computedRangeBitCount); + + final int decodedDifference = ImageEncoder + .decodeValueFromGivenBits(encodedDifference, + computedRange, computedRangeBitCount); + + decodedValue = averageDecodedValue - decodedDifference; + if (decodedValue > 255) + decodedValue = 255; + if (decodedValue < 0) + decodedValue = 0; + } + } else + decodedRangeMap[index] = (byte) inheritedRange; + decodedMap[index] = (byte) decodedValue; + return decodedValue; + } - decodedValue = averageDecodedValue - decodedDifference; - if (decodedValue > 255) decodedValue = 255; - if (decodedValue < 0) decodedValue = 0; - } - } else { - decodedRangeMap[index] = (byte)inheritedRange; - } - decodedMap[index] = (byte)decodedValue; - return decodedValue; - } + private void loadPixel(final int step, final int offsetX, final int offsetY, + final int x, final int y, final byte[] pixels, + final int averageDecodedY, final int averageDecodedU, + final int averageDecodedV) throws IOException { + + final int index = (y * width) + x; + + 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; + + final int colorBufferIndex = index * 3; + + final Color color = new Color(); + color.y = loadChannel(decodedYRangeMap, decodedYMap, + approximator.yTable, averageDecodedY, index, parentIndex); + color.u = loadChannel(decodedURangeMap, decodedUMap, + approximator.uTable, averageDecodedU, index, parentIndex); + color.v = loadChannel(decodedVRangeMap, decodedVMap, + approximator.vTable, averageDecodedV, index, parentIndex); + + color.YUV2RGB(); + + pixels[colorBufferIndex] = (byte) color.r; + pixels[colorBufferIndex + 1] = (byte) color.g; + pixels[colorBufferIndex + 2] = (byte) color.b; + + } }