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=5b0e8db5f85a28a5b8607c118aa0a6d73091404b;hp=7e8af8d969f62828289bf59662067960d4eb95dc;hb=a4acb4f31760b0c654f9a041eef9797782d6043a;hpb=be13f5e7ec2b5e610db95ab2950a35a4e64c9a71 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; - } - } - }