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=53f2d9125be148544ad2dcf95fe5873065dc324c;hp=37f3c0e4c6ac0f37081717fd485038f7ada83752;hb=a4acb4f31760b0c654f9a041eef9797782d6043a;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..53f2d91 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageDecoder.java @@ -1,3 +1,12 @@ +/* + * 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; /** @@ -8,225 +17,235 @@ import java.awt.image.DataBufferByte; import java.awt.image.WritableRaster; import java.io.IOException; +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; + byte[] decodedYRangeMap; - byte [] decodedYRangeMap; - byte [] decodedYMap; + byte[] decodedYMap; + byte[] decodedURangeMap; - byte [] decodedURangeMap; - byte [] decodedUMap; + byte[] decodedUMap; + byte[] decodedVRangeMap; - byte [] decodedVRangeMap; - byte [] decodedVMap; + byte[] decodedVMap; Color tmpColor = new Color(); - Approximator approximator; + BitInputStream bitInputStream; - ColorStats colorStats = new ColorStats(); + OperatingContext context = new OperatingContext(); - - public ImageDecoder (Image image, BitInputStream bitInputStream) { + + public ImageDecoder(final Image image, final BitInputStream bitInputStream) { approximator = new Approximator(); - this.image = image; + this.image = image; this.bitInputStream = bitInputStream; width = image.metaData.width; height = image.metaData.height; decodedYRangeMap = new byte[width * height]; - decodedYRangeMap[0] = (byte)(255); + decodedYRangeMap[0] = (byte) (255); decodedYMap = new byte[width * height]; decodedURangeMap = new byte[width * height]; - decodedURangeMap[0] = (byte)(255); + decodedURangeMap[0] = (byte) (255); decodedUMap = new byte[width * height]; decodedVRangeMap = new byte[width * height]; - decodedVRangeMap[0] = (byte)(255); + 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(); + + final WritableRaster raster = image.bufferedImage.getRaster(); + final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); + final 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); + + 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(); - - pixels[0] = (byte)color.r; - pixels[0+1] = (byte)color.g; - pixels[0+2] = (byte)color.b; - + 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) { + if (width > height) largestDimension = width; - } else { + else largestDimension = height; - } - - while (initialStep < largestDimension){ + + while (initialStep < largestDimension) initialStep = initialStep * 2; - } grid(initialStep, pixels); } - - public void grid(int step, byte [] pixels) throws IOException { + public 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); - if (step > 2) grid(step / 2, pixels); + if (step > 2) + grid(step / 2, pixels); } + public void gridDiagonal(final int offsetX, final int offsetY, + final int step, final byte[] pixels) throws IOException { - 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){ + for (int y = offsetY; y < height; y = y + step) + for (int x = offsetX; x < width; x = x + step) { + final int halfStep = step / 2; - 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.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 gridSquare(final int offsetX, final int offsetY, + final int step, final byte[] pixels) throws IOException { - 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) { - for (int y = offsetY; y < height; y = y + step){ - for (int x = offsetX; x < width; x = x + step){ - - int halfStep = step / 2; + 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.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; - 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; + final int inheritedRange = ImageEncoder + .byteToInt(decodedRangeMap[parentIndex]); + int computedRange = inheritedRange; - int halfStep = step / 2; + final int bitCount = table.proposeBitcountForRange(inheritedRange); + int computedRangeBitCount = 0; + if (bitCount > 0) { - 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 rangeDecreases = bitInputStream.readBits(1); + if (rangeDecreases != 0) + computedRange = table.proposeDecreasedRange(inheritedRange); + decodedRangeMap[index] = (byte) computedRange; + computedRangeBitCount = table + .proposeBitcountForRange(computedRange); + if (computedRangeBitCount > 0) { - int colorBufferIndex = index * 3; + final int encodedDifference = bitInputStream + .readBits(computedRangeBitCount); - 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; + 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; } + public 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 { - private int loadChannel(byte [] decodedRangeMap, byte [] decodedMap, Table table, int averageDecodedValue, int index, - int parentIndex) throws IOException { - int decodedValue = averageDecodedValue; - - int inheritedRange = ImageEncoder.byteToInt(decodedRangeMap[parentIndex]); - int computedRange = inheritedRange; + final int index = (y * width) + x; - int bitCount = table.proposeBitcountForRange(inheritedRange); - int computedRangeBitCount = 0; - if ( bitCount > 0){ + final int halfStep = step / 2; - int rangeDecreases = bitInputStream.readBits(1); - if (rangeDecreases != 0){ - computedRange = table.proposeDecreasedRange(inheritedRange); - } + 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; - decodedRangeMap[index] = (byte)computedRange; - computedRangeBitCount = table.proposeBitcountForRange(computedRange); + final int colorBufferIndex = index * 3; - if (computedRangeBitCount > 0){ + 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); - int encodedDifference = bitInputStream.readBits(computedRangeBitCount); + color.YUV2RGB(); - int decodedDifference = ImageEncoder.decodeValueFromGivenBits(encodedDifference, computedRange, computedRangeBitCount); + pixels[colorBufferIndex] = (byte) color.r; + pixels[colorBufferIndex + 1] = (byte) color.g; + pixels[colorBufferIndex + 2] = (byte) color.b; - decodedValue = averageDecodedValue - decodedDifference; - if (decodedValue > 255) decodedValue = 255; - if (decodedValue < 0) decodedValue = 0; - } - } else { - decodedRangeMap[index] = (byte)inheritedRange; - } - decodedMap[index] = (byte)decodedValue; - return decodedValue; } }