X-Git-Url: http://www2.svjatoslav.eu/gitweb/?p=imagesqueeze.git;a=blobdiff_plain;f=src%2Fmain%2Fjava%2Feu%2Fsvjatoslav%2Fimagesqueeze%2Fcodec%2FImageEncoder.java;h=0bf2fa886308d47768f3a4a22a69e1f82414ad50;hp=613064c14e2b717455e5843ee2502a82b0bd1b43;hb=HEAD;hpb=4bcffe8896c08c9f60b2707da71bb39a64618d93 diff --git a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java index 613064c..0bf2fa8 100755 --- a/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java +++ b/src/main/java/eu/svjatoslav/imagesqueeze/codec/ImageEncoder.java @@ -1,548 +1,530 @@ /* - * 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. + * 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 encoder. */ +import eu.svjatoslav.commons.data.BitOutputStream; + import java.awt.image.DataBufferByte; import java.awt.image.WritableRaster; import java.io.IOException; -import eu.svjatoslav.commons.data.BitOutputStream; +class ImageEncoder { + + private final Image image; + private final Approximator approximator; + // ColorStats colorStats = new ColorStats(); + private final OperatingContext context = new OperatingContext(); + private final OperatingContext context2 = new OperatingContext(); + int bitsForY; + int bitsForU; + int bitsForV; + private int width; + private int height; + private Channel yChannel; + private Channel uChannel; + private Channel vChannel; + private BitOutputStream bitOutputStream; + + public ImageEncoder(final Image image) { + approximator = new Approximator(); + + // bitOutputStream = outputStream; + + this.image = image; + + } + + 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; -public class ImageEncoder { + final int realvalueForThisBitcount = 1 << remainingBitCount; - Image image; - int width, height; + // int valueMultiplier = range / realvalueForThisBitcount; + int decodedValue = (range * encodedValue) + / realvalueForThisBitcount; - Channel yChannel; - Channel uChannel; - Channel vChannel; + if (decodedValue > range) + decodedValue = range; - Approximator approximator; + if (negativeBit == 0) + return decodedValue; + else + return -decodedValue; - int bitsForY; - int bitsForU; - int bitsForV; + } + } - // ColorStats colorStats = new ColorStats(); - OperatingContext context = new OperatingContext(); - OperatingContext context2 = new OperatingContext(); + private static int encodeValueIntoGivenBits(int value, final int range, + final int bitCount) { - BitOutputStream bitOutputStream; + int negativeBit = 0; - public ImageEncoder(final Image image) { - approximator = new Approximator(); + if (value < 0) { + negativeBit = 1; + value = -value; + } - // bitOutputStream = outputStream; + final int remainingBitCount = bitCount - 1; - this.image = image; + if (remainingBitCount == 0) + return negativeBit; + else { + // still one or more bits left, encode value as precisely as + // possible - } + if (value > range) + value = range; - public void encode(final BitOutputStream bitOutputStream) - throws IOException { - this.bitOutputStream = bitOutputStream; + final int realvalueForThisBitcount = 1 << remainingBitCount; + // int valueMultiplier = range / realvalueForThisBitcount; + int encodedValue = (value * realvalueForThisBitcount) / range; - approximator.initialize(); + if (encodedValue >= realvalueForThisBitcount) + encodedValue = realvalueForThisBitcount - 1; - approximator.save(bitOutputStream); + encodedValue = (encodedValue << 1) + negativeBit; - width = image.metaData.width; - height = image.metaData.height; + return encodedValue; + } + } - final WritableRaster raster = image.bufferedImage.getRaster(); - final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); - final byte[] pixels = dbi.getData(); + public static void storeIntegerCompressed8( + final BitOutputStream outputStream, final int data) + throws IOException { - if (yChannel == null) { - yChannel = new Channel(width, height); - } else { - yChannel.reset(); - } + if (data < 256) { + outputStream.storeBits(0, 1); + outputStream.storeBits(data, 8); + } else { + outputStream.storeBits(1, 1); + outputStream.storeBits(data, 32); + } + } - if (uChannel == null) { - uChannel = new Channel(width, height); - } else { - uChannel.reset(); - } + public void encode(final BitOutputStream bitOutputStream) + throws IOException { + this.bitOutputStream = bitOutputStream; - if (vChannel == null) { - vChannel = new Channel(width, height); - } else { - vChannel.reset(); - } + approximator.initialize(); - // create YUV map out of RGB raster data - final Color color = new Color(); + approximator.save(bitOutputStream); - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { + width = image.metaData.width; + height = image.metaData.height; - final int index = (y * width) + x; - final int colorBufferIndex = index * 3; + final WritableRaster raster = image.bufferedImage.getRaster(); + final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer(); + final byte[] pixels = dbi.getData(); - int blue = pixels[colorBufferIndex]; - if (blue < 0) - blue = blue + 256; + if (yChannel == null) + yChannel = new Channel(width, height); + else + yChannel.reset(); - int green = pixels[colorBufferIndex + 1]; - if (green < 0) - green = green + 256; + if (uChannel == null) + uChannel = new Channel(width, height); + else + uChannel.reset(); - int red = pixels[colorBufferIndex + 2]; - if (red < 0) - red = red + 256; + if (vChannel == null) + vChannel = new Channel(width, height); + else + vChannel.reset(); - color.r = red; - color.g = green; - color.b = blue; + // create YUV map out of RGB raster data + final Color color = new Color(); - color.RGB2YUV(); + for (int y = 0; y < height; y++) + for (int x = 0; x < width; x++) { - yChannel.map[index] = (byte) color.y; - uChannel.map[index] = (byte) color.u; - vChannel.map[index] = (byte) color.v; - } - } + final int index = (y * width) + x; + final int colorBufferIndex = index * 3; - yChannel.decodedMap[0] = yChannel.map[0]; - uChannel.decodedMap[0] = uChannel.map[0]; - vChannel.decodedMap[0] = vChannel.map[0]; + int blue = pixels[colorBufferIndex]; + if (blue < 0) + blue = blue + 256; - bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8); - bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8); - bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8); + int green = pixels[colorBufferIndex + 1]; + if (green < 0) + green = green + 256; - // detect initial step - int largestDimension; - int initialStep = 2; - if (width > height) { - largestDimension = width; - } else { - largestDimension = height; - } + int red = pixels[colorBufferIndex + 2]; + if (red < 0) + red = red + 256; - while (initialStep < largestDimension) { - initialStep = initialStep * 2; - } + color.r = red; + color.g = green; + color.b = blue; - rangeGrid(initialStep); - rangeRoundGrid(2); - saveGrid(initialStep); - } + color.RGB2YUV(); - 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(); - } - - 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); - } - - 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) { - - final int index = (y * width) + x; - final int halfStep = step / 2; + yChannel.map[index] = (byte) color.y; + uChannel.map[index] = (byte) color.u; + vChannel.map[index] = (byte) color.v; + } - context.initialize(image, yChannel.map, uChannel.map, - vChannel.map); + yChannel.decodedMap[0] = yChannel.map[0]; + uChannel.decodedMap[0] = uChannel.map[0]; + vChannel.decodedMap[0] = vChannel.map[0]; - context.measureNeighborEncode(x - halfStep, y - halfStep); - context.measureNeighborEncode(x + halfStep, y - halfStep); - context.measureNeighborEncode(x - halfStep, y + halfStep); - context.measureNeighborEncode(x + halfStep, y + halfStep); + bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8); + bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8); + bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8); - yChannel.rangeMap[index] = (byte) context.getYRange(index); - uChannel.rangeMap[index] = (byte) context.getURange(index); - vChannel.rangeMap[index] = (byte) context.getVRange(index); - } - } - } + // detect initial step + int largestDimension; + int initialStep = 2; + if (width > height) + largestDimension = width; + else + largestDimension = height; - 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) { - - final int index = (y * width) + x; - final int halfStep = step / 2; - - context.initialize(image, yChannel.map, uChannel.map, - vChannel.map); + while (initialStep < largestDimension) + initialStep = initialStep * 2; - context.measureNeighborEncode(x - halfStep, y); - context.measureNeighborEncode(x + halfStep, y); - context.measureNeighborEncode(x, y - halfStep); - context.measureNeighborEncode(x, y + halfStep); + rangeGrid(initialStep); + rangeRoundGrid(2); + saveGrid(initialStep); + } + + 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); - yChannel.rangeMap[index] = (byte) context.getYRange(index); - uChannel.rangeMap[index] = (byte) context.getURange(index); - vChannel.rangeMap[index] = (byte) context.getVRange(index); - } - } - } + if (inheritedBitCount > 0) { + int computedRange; + computedRange = table.proposeRangeForRange(range, inheritedRange); + decodedRangeMap[index] = (byte) computedRange; - public void rangeRoundGrid(final int step) { + 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); - rangeRoundGridDiagonal(step / 2, step / 2, step); - rangeRoundGridSquare(step / 2, 0, step); - rangeRoundGridSquare(0, step / 2, step); + final int decodedDifference = decodeValueFromGivenBits( + bitEncodedDifference, computedRange, computedBitCount); + int decodedValue = averageDecodedValue - decodedDifference; + if (decodedValue > 255) + decodedValue = 255; + if (decodedValue < 0) + decodedValue = 0; - if (step < 1024) - rangeRoundGrid(step * 2); - } + decodedMap[index] = (byte) decodedValue; + } else + decodedMap[index] = (byte) averageDecodedValue; - 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) { + } else { + decodedRangeMap[index] = (byte) inheritedRange; + decodedMap[index] = (byte) averageDecodedValue; + } + } - final int index = (y * width) + x; + public void printStatistics() { + System.out.println("Y channel:"); + yChannel.printStatistics(); - final int yRange = byteToInt(yChannel.rangeMap[index]); - final int uRange = byteToInt(uChannel.rangeMap[index]); - final int vRange = byteToInt(vChannel.rangeMap[index]); + System.out.println("U channel:"); + uChannel.printStatistics(); - final int halfStep = step / 2; + System.out.println("V channel:"); + vChannel.printStatistics(); + } - final int parentIndex = ((y - halfStep) * width) - + (x - halfStep); + private void rangeGrid(final int step) { - int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]); + // gridSquare(step / 2, step / 2, step, pixels); + + rangeGridDiagonal(step / 2, step / 2, step); + rangeGridSquare(step / 2, 0, step); + rangeGridSquare(0, step / 2, step); - if (parentYRange < yRange) { - parentYRange = yRange; - yChannel.rangeMap[parentIndex] = (byte) parentYRange; - } + if (step > 2) + rangeGrid(step / 2); + } - int parentURange = byteToInt(uChannel.rangeMap[parentIndex]); + private 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) { - if (parentURange < uRange) { - parentURange = uRange; - uChannel.rangeMap[parentIndex] = (byte) parentURange; - } + final int index = (y * width) + x; + final int halfStep = step / 2; - int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]); + context.initialize(image, yChannel.map, uChannel.map, + vChannel.map); - if (parentVRange < vRange) { - parentVRange = vRange; - vChannel.rangeMap[parentIndex] = (byte) parentVRange; - } - } - } - } + context.measureNeighborEncode(x - halfStep, y - halfStep); + context.measureNeighborEncode(x + halfStep, y - halfStep); + context.measureNeighborEncode(x - halfStep, y + halfStep); + context.measureNeighborEncode(x + halfStep, y + halfStep); - 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; + yChannel.rangeMap[index] = (byte) context.getYRange(index); + uChannel.rangeMap[index] = (byte) context.getURange(index); + vChannel.rangeMap[index] = (byte) context.getVRange(index); + } + } - final int yRange = byteToInt(yChannel.rangeMap[index]); - final int uRange = byteToInt(uChannel.rangeMap[index]); - final int vRange = byteToInt(vChannel.rangeMap[index]); + private 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) { - final int halfStep = step / 2; + final int index = (y * width) + x; + final int halfStep = step / 2; - int parentIndex; - if (offsetX > 0) { - parentIndex = (y * width) + (x - halfStep); - } else { - parentIndex = ((y - halfStep) * width) + x; - } + context.initialize(image, yChannel.map, uChannel.map, + vChannel.map); - int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]); + context.measureNeighborEncode(x - halfStep, y); + context.measureNeighborEncode(x + halfStep, y); + context.measureNeighborEncode(x, y - halfStep); + context.measureNeighborEncode(x, y + halfStep); - if (parentYRange < yRange) { - parentYRange = yRange; - yChannel.rangeMap[parentIndex] = (byte) parentYRange; - } + yChannel.rangeMap[index] = (byte) context.getYRange(index); + uChannel.rangeMap[index] = (byte) context.getURange(index); + vChannel.rangeMap[index] = (byte) context.getVRange(index); + } + } - int parentURange = byteToInt(uChannel.rangeMap[parentIndex]); - - if (parentURange < uRange) { - parentURange = uRange; - uChannel.rangeMap[parentIndex] = (byte) parentURange; - } - - int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]); - - if (parentVRange < vRange) { - parentVRange = vRange; - vChannel.rangeMap[parentIndex] = (byte) parentVRange; - } + private void rangeRoundGrid(final int step) { - } - } - } + rangeRoundGridDiagonal(step / 2, step / 2, step); + rangeRoundGridSquare(step / 2, 0, step); + rangeRoundGridSquare(0, step / 2, step); - public void saveGrid(final int step) throws IOException { + if (step < 1024) + rangeRoundGrid(step * 2); + } - saveGridDiagonal(step / 2, step / 2, step); - saveGridSquare(step / 2, 0, step); - saveGridSquare(0, step / 2, step); + private 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) { - if (step > 2) - saveGrid(step / 2); - } + final int index = (y * width) + x; - 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 yRange = byteToInt(yChannel.rangeMap[index]); + final int uRange = byteToInt(uChannel.rangeMap[index]); + final int vRange = byteToInt(vChannel.rangeMap[index]); - final int halfStep = step / 2; + final int halfStep = step / 2; - 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); + final int parentIndex = ((y - halfStep) * width) + + (x - halfStep); - savePixel(step, offsetX, offsetY, x, y, - context2.colorStats.getAverageY(), - context2.colorStats.getAverageU(), - context2.colorStats.getAverageV()); + int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]); - } - } - } + if (parentYRange < yRange) { + parentYRange = yRange; + yChannel.rangeMap[parentIndex] = (byte) parentYRange; + } - 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) { + int parentURange = byteToInt(uChannel.rangeMap[parentIndex]); - final 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); + if (parentURange < uRange) { + parentURange = uRange; + uChannel.rangeMap[parentIndex] = (byte) parentURange; + } - savePixel(step, offsetX, offsetY, x, y, - context2.colorStats.getAverageY(), - context2.colorStats.getAverageU(), - context2.colorStats.getAverageV()); + int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]); - } - } - } + if (parentVRange < vRange) { + parentVRange = vRange; + vChannel.rangeMap[parentIndex] = (byte) parentVRange; + } + } + } - 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 { + private 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; + final int index = (y * width) + x; - final int py = byteToInt(yChannel.map[index]); - final int pu = byteToInt(uChannel.map[index]); - final int pv = byteToInt(vChannel.map[index]); + final int yRange = byteToInt(yChannel.rangeMap[index]); + final int uRange = byteToInt(uChannel.rangeMap[index]); + final 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]); + final 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; - 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; - } + int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]); - encodeChannel(approximator.yTable, yChannel, averageDecodedY, index, - py, yRange, parentIndex); + if (parentYRange < yRange) { + parentYRange = yRange; + yChannel.rangeMap[parentIndex] = (byte) parentYRange; + } - encodeChannel(approximator.uTable, uChannel, averageDecodedU, index, - pu, uRange, parentIndex); + int parentURange = byteToInt(uChannel.rangeMap[parentIndex]); - encodeChannel(approximator.vTable, vChannel, averageDecodedV, index, - pv, vRange, parentIndex); + if (parentURange < uRange) { + parentURange = uRange; + uChannel.rangeMap[parentIndex] = (byte) parentURange; + } - } + int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]); - public static int byteToInt(final byte input) { - int result = input; - if (result < 0) - result = result + 256; - return result; - } + if (parentVRange < vRange) { + parentVRange = vRange; + vChannel.rangeMap[parentIndex] = (byte) parentVRange; + } - public static int decodeValueFromGivenBits(final int encodedBits, - final int range, final int bitCount) { - final int negativeBit = encodedBits & 1; + } + } - final int remainingBitCount = bitCount - 1; + private void saveGrid(final int step) throws IOException { - if (remainingBitCount == 0) { - // no more bits remaining to encode actual value + saveGridDiagonal(step / 2, step / 2, step); + saveGridSquare(step / 2, 0, step); + saveGridSquare(0, step / 2, step); - if (negativeBit == 0) { - return range; - } else { - return -range; - } + if (step > 2) + saveGrid(step / 2); + } - } else { - // still one or more bits left, encode value as precisely as - // possible + private 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 encodedValue = (encodedBits >>> 1) + 1; + final int halfStep = step / 2; - final int realvalueForThisBitcount = 1 << remainingBitCount; + 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); - // int valueMultiplier = range / realvalueForThisBitcount; - int decodedValue = (range * encodedValue) - / realvalueForThisBitcount; + savePixel(step, offsetX, offsetY, x, y, + context2.colorStats.getAverageY(), + context2.colorStats.getAverageU(), + context2.colorStats.getAverageV()); - if (decodedValue > range) - decodedValue = range; + } + } - if (negativeBit == 0) { - return decodedValue; - } else { - return -decodedValue; - } + private 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; - public static int encodeValueIntoGivenBits(int value, final int range, - final int bitCount) { + 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); - int negativeBit = 0; + savePixel(step, offsetX, offsetY, x, y, + context2.colorStats.getAverageY(), + context2.colorStats.getAverageU(), + context2.colorStats.getAverageV()); - if (value < 0) { - negativeBit = 1; - value = -value; - } + } + } - final int remainingBitCount = bitCount - 1; + private 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 { - if (remainingBitCount == 0) { - // no more bits remaining to encode actual value + final int index = (y * width) + x; - return negativeBit; + final int py = byteToInt(yChannel.map[index]); + final int pu = byteToInt(uChannel.map[index]); + final int pv = byteToInt(vChannel.map[index]); - } else { - // still one or more bits left, encode value as precisely as - // possible + final int yRange = byteToInt(yChannel.rangeMap[index]); + final int uRange = byteToInt(uChannel.rangeMap[index]); + final int vRange = byteToInt(vChannel.rangeMap[index]); - if (value > range) - value = range; + final int halfStep = step / 2; - final int realvalueForThisBitcount = 1 << remainingBitCount; - // int valueMultiplier = range / realvalueForThisBitcount; - int encodedValue = (value * realvalueForThisBitcount) / range; + 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 (encodedValue >= realvalueForThisBitcount) - encodedValue = realvalueForThisBitcount - 1; + encodeChannel(approximator.yTable, yChannel, averageDecodedY, index, + py, yRange, parentIndex); + + encodeChannel(approximator.uTable, uChannel, averageDecodedU, index, + pu, uRange, parentIndex); - encodedValue = (encodedValue << 1) + negativeBit; + encodeChannel(approximator.vTable, vChannel, averageDecodedV, index, + pv, vRange, parentIndex); - return encodedValue; - } - } + } }