-bin/
-target/
-.settings/
+/bin/
+/target/
+/.settings/
/.project
/.classpath
+/.idea/
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
-<meta
- http-equiv="Content-Type"
- content="text/html; charset=UTF-8">
-<title>ImageSqueeze</title>
+ <meta
+ http-equiv="Content-Type"
+ content="text/html; charset=UTF-8">
+ <title>ImageSqueeze</title>
</head>
<body>
- <h1>ImageSqueeze - lossy image codec</h1>
- <a href="http://php.svjatoslav.eu/gitweb/?p=imagesqueeze.git;a=snapshot;h=HEAD;sf=tgz">Download</a>
-
- <a href="http://www2.svjatoslav.eu/gitbrowse/imagesqueeze/doc/index.html">Online homepage</a>
-
- <a href="http://svjatoslav.eu/programs.jsp">Other applications hosted on svjatoslav.eu</a>
+<h1>ImageSqueeze - lossy image codec</h1>
+<a href="http://php.svjatoslav.eu/gitweb/?p=imagesqueeze.git;a=snapshot;h=HEAD;sf=tgz">Download</a>
+
+<a href="http://www2.svjatoslav.eu/gitbrowse/imagesqueeze/doc/index.html">Online homepage</a>
+
+<a href="http://svjatoslav.eu/programs.jsp">Other applications hosted on svjatoslav.eu</a>
<pre>
<b>Program author:</b>
Svjatoslav Agejenko
Below are original photo and the same image being compressed down to ~93 Kb and then decompressed.
-<img src="originalAndCompressed.png" />
+<img src="originalAndCompressed.png"/>
When looking very closely, slight grainyness, loss of color precision and
blurriness (loss of detail) could be noticed as a compression artifacts.
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/target/classes" />
+ <output-test url="file://$MODULE_DIR$/target/test-classes" />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Maven: eu.svjatoslav:svjatoslavcommons:1.5-SNAPSHOT" level="project" />
+ </component>
+</module>
\ No newline at end of file
<project
- xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>eu.svjatoslav</groupId>
<artifactId>imagesqueeze</artifactId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.1</version>
<configuration>
- <source>1.6</source>
- <target>1.6</target>
+ <source>1.8</source>
+ <target>1.8</target>
<optimize>true</optimize>
<encoding>UTF-8</encoding>
</configuration>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
- <artifactId>wagon-ssh</artifactId>
- <version>2.2</version>
+ <artifactId>wagon-ssh-external</artifactId>
+ <version>2.6</version>
</extension>
</extensions>
</build>
</dependency>
</dependencies>
+ <distributionManagement>
+ <snapshotRepository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven</url>
+ </snapshotRepository>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scpexe://svjatoslav.eu/var/www/svjatoslav.eu/maven</url>
+ </repository>
+ </distributionManagement>
+
<repositories>
<repository>
<id>svjatoslav.eu</id>
</repository>
</repositories>
-
- <distributionManagement>
- <snapshotRepository>
- <id>svjatoslav.eu</id>
- <name>svjatoslav.eu</name>
- <url>scp://svjatoslav.eu:7022/var/www/maven</url>
- </snapshotRepository>
- </distributionManagement>
+ <scm>
+ <connection>scm:git:ssh://git@svjatoslav.eu/home/git/repositories/imagesqueeze.git</connection>
+ <developerConnection>scm:git:ssh://git@svjatoslav.eu/home/git/repositories/imagesqueeze.git
+ </developerConnection>
+ </scm>
</project>
package eu.svjatoslav.imagesqueeze.codec;
-import java.io.IOException;
-
import eu.svjatoslav.commons.data.BitInputStream;
import eu.svjatoslav.commons.data.BitOutputStream;
+import java.io.IOException;
+
/**
* Since it is a lossy codec, instead of storing exact values, approximated
* values are stored to save on bit count.
*/
-public class Approximator implements Comparable<Approximator> {
-
- public Table yTable = new Table();
- public Table uTable = new Table();
- public Table vTable = new Table();
-
- public Approximator() {
- }
-
- @Override
- public int compareTo(final Approximator o) {
- int result = yTable.compareTo(o.yTable);
- if (result != 0) {
- return result;
- }
-
- result = uTable.compareTo(o.uTable);
- if (result != 0) {
- return result;
- }
-
- result = vTable.compareTo(o.vTable);
- return result;
- }
-
- public void computeLookupTables() {
- yTable.computeLookupTables();
- uTable.computeLookupTables();
- vTable.computeLookupTables();
- }
-
- public void initialize() {
- yTable.reset();
- uTable.reset();
- vTable.reset();
-
- yTable.addEntry(0, 6, 0);
- yTable.addEntry(27, 30, 4);
- yTable.addEntry(255, 255, 6);
-
- uTable.addEntry(0, 9, 0);
- uTable.addEntry(27, 30, 4);
- uTable.addEntry(255, 255, 6);
-
- vTable.addEntry(0, 9, 0);
- vTable.addEntry(27, 30, 4);
- vTable.addEntry(255, 255, 6);
-
- computeLookupTables();
- }
-
- public void load(final BitInputStream inputStream) throws IOException {
- yTable.load(inputStream);
- uTable.load(inputStream);
- vTable.load(inputStream);
- }
-
- public void save(final BitOutputStream outputStream) throws IOException {
- yTable.save(outputStream);
- uTable.save(outputStream);
- vTable.save(outputStream);
- }
+class Approximator {
+
+ public final Table yTable = new Table();
+ public final Table uTable = new Table();
+ public final Table vTable = new Table();
+
+ public Approximator() {
+ }
+
+
+ public void computeLookupTables() {
+ yTable.computeLookupTables();
+ uTable.computeLookupTables();
+ vTable.computeLookupTables();
+ }
+
+ public void initialize() {
+ yTable.reset();
+ uTable.reset();
+ vTable.reset();
+
+ yTable.addEntry(0, 6, 0);
+ yTable.addEntry(27, 30, 4);
+ yTable.addEntry(255, 255, 6);
+
+ uTable.addEntry(0, 9, 0);
+ uTable.addEntry(27, 30, 4);
+ uTable.addEntry(255, 255, 6);
+
+ vTable.addEntry(0, 9, 0);
+ vTable.addEntry(27, 30, 4);
+ vTable.addEntry(255, 255, 6);
+
+ computeLookupTables();
+ }
+
+ public void load(final BitInputStream inputStream) throws IOException {
+ yTable.load(inputStream);
+ uTable.load(inputStream);
+ vTable.load(inputStream);
+ }
+
+ public void save(final BitOutputStream outputStream) throws IOException {
+ yTable.save(outputStream);
+ uTable.save(outputStream);
+ vTable.save(outputStream);
+ }
}
package eu.svjatoslav.imagesqueeze.codec;
-public class Channel {
+class Channel {
- byte[] rangeMap;
- byte[] map;
+ final byte[] rangeMap;
+ final byte[] map;
- byte[] decodedRangeMap;
- byte[] decodedMap;
+ final byte[] decodedRangeMap;
+ final byte[] decodedMap;
- int bitCount;
+ int bitCount;
- public Channel(final int width, final int height) {
- rangeMap = new byte[width * height];
+ public Channel(final int width, final int height) {
+ rangeMap = new byte[width * height];
- map = new byte[width * height];
+ map = new byte[width * height];
- decodedRangeMap = new byte[width * height];
- decodedRangeMap[0] = (byte) 255;
+ decodedRangeMap = new byte[width * height];
+ decodedRangeMap[0] = (byte) 255;
- decodedMap = new byte[width * height];
- };
+ decodedMap = new byte[width * height];
+ }
- public void printStatistics() {
- final float bitsPerPixel = (float) bitCount / (float) rangeMap.length;
- System.out.println((bitCount / 8) + " bytes. " + bitsPerPixel
- + " bits per pixel.");
- }
+ public void printStatistics() {
+ final float bitsPerPixel = (float) bitCount / (float) rangeMap.length;
+ System.out.println((bitCount / 8) + " bytes. " + bitsPerPixel
+ + " bits per pixel.");
+ }
- public void reset() {
+ public void reset() {
- for (int i = 0; i < decodedMap.length; i++) {
- decodedMap[i] = 0;
- }
+ for (int i = 0; i < decodedMap.length; i++) {
+ decodedMap[i] = 0;
+ }
- for (int i = 0; i < decodedRangeMap.length; i++) {
- decodedRangeMap[i] = 0;
- }
- decodedRangeMap[0] = (byte) 255;
+ for (int i = 0; i < decodedRangeMap.length; i++) {
+ decodedRangeMap[i] = 0;
+ }
+ decodedRangeMap[0] = (byte) 255;
- for (int i = 0; i < map.length; i++) {
- map[i] = 0;
- }
+ for (int i = 0; i < map.length; i++) {
+ map[i] = 0;
+ }
- for (int i = 0; i < rangeMap.length; i++) {
- rangeMap[i] = 0;
- }
+ for (int i = 0; i < rangeMap.length; i++) {
+ rangeMap[i] = 0;
+ }
- bitCount = 0;
- }
+ bitCount = 0;
+ }
}
/**
* Helper class to convert between RGB and YUV
*/
-public class Color {
+class Color {
- int r;
- int g;
- int b;
+ int r;
+ int g;
+ int b;
- int y;
- int u;
- int v;
+ int y;
+ int u;
+ int v;
- public void RGB2YUV() {
+ public void RGB2YUV() {
- y = (int) ((r * 0.299000) + (g * 0.587000) + (b * 0.114000));
- u = (int) ((r * -0.168736) + (g * -0.331264) + (b * 0.500000) + 128);
- v = (int) ((r * 0.500000) + (g * -0.418688) + (b * -0.081312) + 128);
+ y = (int) ((r * 0.299000) + (g * 0.587000) + (b * 0.114000));
+ u = (int) ((r * -0.168736) + (g * -0.331264) + (b * 0.500000) + 128);
+ v = (int) ((r * 0.500000) + (g * -0.418688) + (b * -0.081312) + 128);
- if (y < 0)
- y = 0;
- if (u < 0)
- u = 0;
- if (v < 0)
- v = 0;
+ if (y < 0)
+ y = 0;
+ if (u < 0)
+ u = 0;
+ if (v < 0)
+ v = 0;
- if (y > 255)
- y = 255;
- if (u > 255)
- u = 255;
- if (v > 255)
- v = 255;
+ if (y > 255)
+ y = 255;
+ if (u > 255)
+ u = 255;
+ if (v > 255)
+ v = 255;
- }
+ }
- public void YUV2RGB() {
+ public void YUV2RGB() {
- b = (int) (y + (1.4075 * (v - 128)));
- g = (int) (y - (0.3455 * (u - 128)) - (0.7169 * (v - 128)));
- r = (int) (y + (1.7790 * (u - 128)));
+ b = (int) (y + (1.4075 * (v - 128)));
+ g = (int) (y - (0.3455 * (u - 128)) - (0.7169 * (v - 128)));
+ r = (int) (y + (1.7790 * (u - 128)));
- if (r < 0)
- r = 0;
- if (g < 0)
- g = 0;
- if (b < 0)
- b = 0;
+ if (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
- if (r > 255)
- r = 255;
- if (g > 255)
- g = 255;
- if (b > 255)
- b = 255;
+ if (r > 255)
+ r = 255;
+ if (g > 255)
+ g = 255;
+ if (b > 255)
+ b = 255;
- }
+ }
-};
+}
package eu.svjatoslav.imagesqueeze.codec;
-public class ColorStats {
+class ColorStats {
- int ySum;
- int uSum;
- int vSum;
+ int ySum;
+ int uSum;
+ int vSum;
- int pixelCount;
+ int pixelCount;
- public ColorStats() {
- reset();
- }
+ public ColorStats() {
+ reset();
+ }
- public int getAverageU() {
- return uSum / pixelCount;
- }
+ public int getAverageU() {
+ return uSum / pixelCount;
+ }
- public int getAverageV() {
- return vSum / pixelCount;
- }
+ public int getAverageV() {
+ return vSum / pixelCount;
+ }
- public int getAverageY() {
- return ySum / pixelCount;
- }
+ public int getAverageY() {
+ return ySum / pixelCount;
+ }
- public void reset() {
- ySum = 0;
- uSum = 0;
- vSum = 0;
- pixelCount = 0;
- }
+ public void reset() {
+ ySum = 0;
+ uSum = 0;
+ vSum = 0;
+ pixelCount = 0;
+ }
}
package eu.svjatoslav.imagesqueeze.codec;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
import eu.svjatoslav.commons.data.BitInputStream;
import eu.svjatoslav.commons.data.BitOutputStream;
+import java.awt.image.BufferedImage;
+import java.io.*;
+
/**
* Main class representing compressed image.
*/
public class Image {
- public ImageMetaData metaData;
-
- public BufferedImage bufferedImage;
-
- ImageEncoder encoder;
+ public ImageMetaData metaData;
- public Image() {
- };
+ public BufferedImage bufferedImage;
- /**
- * Initialize imagesqueeze image based on {@link BufferedImage}.
- * {@link BufferedImage} must be of type BufferedImage.TYPE_3BYTE_BGR .
- */
- public Image(final BufferedImage image) {
+ private ImageEncoder encoder;
- bufferedImage = image;
- metaData = new ImageMetaData();
+ public Image() {
+ }
- metaData.version = 1;
- metaData.width = image.getWidth();
- metaData.height = image.getHeight();
- }
+ /**
+ * Initialize imagesqueeze image based on {@link BufferedImage}.
+ * {@link BufferedImage} must be of type BufferedImage.TYPE_3BYTE_BGR .
+ */
+ public Image(final BufferedImage image) {
- /**
- * Initialize empty imagesqueeze image.
- */
- public Image(final int width, final int height) {
+ bufferedImage = image;
+ metaData = new ImageMetaData();
- bufferedImage = new BufferedImage(width, height,
- BufferedImage.TYPE_3BYTE_BGR);
- metaData = new ImageMetaData();
+ metaData.version = 1;
+ metaData.width = image.getWidth();
+ metaData.height = image.getHeight();
+ }
- metaData.version = 1;
- metaData.width = width;
- metaData.height = height;
- }
+ /**
+ * Initialize empty imagesqueeze image.
+ */
+ public Image(final int width, final int height) {
- /**
- * Load ImgSqz image from {@link File}.
- */
- public void loadImage(final File source) throws IOException {
+ bufferedImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_3BYTE_BGR);
+ metaData = new ImageMetaData();
- final byte[] fileContent = new byte[(int) source.length()];
+ metaData.version = 1;
+ metaData.width = width;
+ metaData.height = height;
+ }
- final FileInputStream fileInputStream = new FileInputStream(source);
+ /**
+ * Load ImgSqz image from {@link File}.
+ */
+ public void loadImage(final File source) throws IOException {
- fileInputStream.read(fileContent);
+ final byte[] fileContent = new byte[(int) source.length()];
- fileInputStream.close();
+ try (FileInputStream fileInputStream = new FileInputStream(source)) {
+ if (fileInputStream.read(fileContent) != fileContent.length)
+ throw new RuntimeException("Failed to read file content");
+ }
- final ByteArrayInputStream inputStream = new ByteArrayInputStream(
- fileContent);
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(
+ fileContent);
- loadImage(inputStream);
- }
+ loadImage(inputStream);
+ }
- /**
- * Load ImgSqz image from {@link InputStream}.
- */
- public void loadImage(final InputStream source) throws IOException {
- final BitInputStream bitInputStream = new BitInputStream(source);
+ /**
+ * Load ImgSqz image from {@link InputStream}.
+ */
+ public void loadImage(final InputStream source) throws IOException {
+ final BitInputStream bitInputStream = new BitInputStream(source);
- metaData = new ImageMetaData();
- metaData.load(bitInputStream);
+ metaData = new ImageMetaData();
+ metaData.load(bitInputStream);
- bufferedImage = new BufferedImage(metaData.width, metaData.height,
- BufferedImage.TYPE_3BYTE_BGR);
+ bufferedImage = new BufferedImage(metaData.width, metaData.height,
+ BufferedImage.TYPE_3BYTE_BGR);
- final ImageDecoder imageDecoder = new ImageDecoder(this, bitInputStream);
+ final ImageDecoder imageDecoder = new ImageDecoder(this, bitInputStream);
- imageDecoder.decode();
- }
+ imageDecoder.decode();
+ }
- /**
- * Save image into ImgSqz file format.
- */
- public void saveImage(final File file) throws IOException {
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- saveImage(outputStream);
+ /**
+ * Save image into ImgSqz file format.
+ */
+ public void saveImage(final File file) throws IOException {
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ saveImage(outputStream);
- final byte[] buffer = outputStream.toByteArray();
- final FileOutputStream fileOutputStream = new FileOutputStream(file);
- fileOutputStream.write(buffer);
- fileOutputStream.close();
- }
+ final byte[] buffer = outputStream.toByteArray();
+ try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
+ fileOutputStream.write(buffer);
+ }
+ }
- /**
- * Save image into ImgSqz file format.
- */
- public void saveImage(final OutputStream outputStream) throws IOException {
+ /**
+ * Save image into ImgSqz file format.
+ */
+ private void saveImage(final OutputStream outputStream) throws IOException {
- final BitOutputStream bitOutputStream = new BitOutputStream(
- outputStream);
+ final BitOutputStream bitOutputStream = new BitOutputStream(
+ outputStream);
- metaData.save(bitOutputStream);
+ metaData.save(bitOutputStream);
- if (encoder == null) {
- encoder = new ImageEncoder(this);
- }
+ if (encoder == null) {
+ encoder = new ImageEncoder(this);
+ }
- encoder.encode(bitOutputStream);
+ encoder.encode(bitOutputStream);
- bitOutputStream.finishByte();
- }
+ bitOutputStream.finishByte();
+ }
}
* Compressed image pixels decoder.
*/
+import eu.svjatoslav.commons.data.BitInputStream;
+
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[] 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(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];
-
- }
-
- public void decode() throws IOException {
- approximator.load(bitInputStream);
- approximator.computeLookupTables();
-
- final WritableRaster raster = image.bufferedImage.getRaster();
- final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
- final byte[] pixels = dbi.getData();
+class ImageDecoder {
- // load top-, left-most pixel.
- decodedYMap[0] = (byte) bitInputStream.readBits(8);
- decodedUMap[0] = (byte) bitInputStream.readBits(8);
- decodedVMap[0] = (byte) bitInputStream.readBits(8);
+ 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();
- final Color color = new Color();
- color.y = ImageEncoder.byteToInt(decodedYMap[0]);
- color.u = ImageEncoder.byteToInt(decodedUMap[0]);
- color.v = ImageEncoder.byteToInt(decodedVMap[0]);
+ public ImageDecoder(final Image image, final BitInputStream bitInputStream) {
+ approximator = new Approximator();
- color.YUV2RGB();
+ this.image = image;
+ this.bitInputStream = bitInputStream;
- pixels[0] = (byte) color.r;
- pixels[0 + 1] = (byte) color.g;
- pixels[0 + 2] = (byte) color.b;
+ width = image.metaData.width;
+ height = image.metaData.height;
- // detect initial step
- int largestDimension;
- int initialStep = 2;
- if (width > height)
- largestDimension = width;
- else
- largestDimension = height;
+ decodedYRangeMap = new byte[width * height];
+ decodedYRangeMap[0] = (byte) (255);
+ decodedYMap = new byte[width * height];
- while (initialStep < largestDimension)
- initialStep = initialStep * 2;
+ decodedURangeMap = new byte[width * height];
+ decodedURangeMap[0] = (byte) (255);
+ decodedUMap = new byte[width * height];
- grid(initialStep, pixels);
- }
+ decodedVRangeMap = new byte[width * height];
+ decodedVRangeMap[0] = (byte) (255);
+ decodedVMap = new byte[width * height];
- 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);
+ public static int readIntegerCompressed8(final BitInputStream inputStream)
+ throws IOException {
- if (step > 2)
- grid(step / 2, pixels);
- }
+ if (inputStream.readBits(1) == 0)
+ return inputStream.readBits(8);
+ else
+ return inputStream.readBits(32);
+ }
- public void gridDiagonal(final int offsetX, final int offsetY,
- final int step, final byte[] pixels) throws IOException {
+ public void decode() throws IOException {
+ approximator.load(bitInputStream);
+ approximator.computeLookupTables();
- for (int y = offsetY; y < height; y = y + step)
- for (int x = offsetX; x < width; x = x + step) {
+ final WritableRaster raster = image.bufferedImage.getRaster();
+ final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+ final byte[] pixels = dbi.getData();
- final int halfStep = step / 2;
+ // load top-, left-most pixel.
+ decodedYMap[0] = (byte) bitInputStream.readBits(8);
+ decodedUMap[0] = (byte) bitInputStream.readBits(8);
+ decodedVMap[0] = (byte) bitInputStream.readBits(8);
- 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);
+ final Color color = new Color();
+ color.y = ImageEncoder.byteToInt(decodedYMap[0]);
+ color.u = ImageEncoder.byteToInt(decodedUMap[0]);
+ color.v = ImageEncoder.byteToInt(decodedVMap[0]);
- loadPixel(step, offsetX, offsetY, x, y, pixels,
- context.colorStats.getAverageY(),
- context.colorStats.getAverageU(),
- context.colorStats.getAverageV());
+ color.YUV2RGB();
- }
- }
+ pixels[0] = (byte) color.r;
+ pixels[0 + 1] = (byte) color.g;
+ pixels[0 + 2] = (byte) color.b;
- public void gridSquare(final int offsetX, final int offsetY,
- final int step, final byte[] pixels) throws IOException {
+ // detect initial step
+ int largestDimension;
+ int initialStep = 2;
+ if (width > height)
+ largestDimension = width;
+ else
+ largestDimension = height;
+
+ while (initialStep < largestDimension)
+ initialStep = initialStep * 2;
- for (int y = offsetY; y < height; y = y + step)
- for (int x = offsetX; x < width; x = x + step) {
+ 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);
- 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 = 0;
- 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;
- }
+ 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;
+ }
- 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 {
-
- 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;
-
- }
+ 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;
+
+ }
}
* 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;
-
-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;
+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 static int encodeValueIntoGivenBits(int value, final int range,
- final int bitCount) {
+ public ImageEncoder(final Image image) {
+ approximator = new Approximator();
- int negativeBit = 0;
+ // bitOutputStream = outputStream;
- if (value < 0) {
- negativeBit = 1;
- value = -value;
- }
+ this.image = image;
- final int remainingBitCount = bitCount - 1;
+ }
- if (remainingBitCount == 0)
- return negativeBit;
- else {
- // still one or more bits left, encode value as precisely as
- // possible
+ public static int byteToInt(final byte input) {
+ int result = input;
+ if (result < 0)
+ result = result + 256;
+ return result;
+ }
- if (value > range)
- value = range;
+ public static int decodeValueFromGivenBits(final int encodedBits,
+ final int range, final int bitCount) {
+ final int negativeBit = encodedBits & 1;
- final int realvalueForThisBitcount = 1 << remainingBitCount;
- // int valueMultiplier = range / realvalueForThisBitcount;
- int encodedValue = (value * realvalueForThisBitcount) / range;
+ final int remainingBitCount = bitCount - 1;
- if (encodedValue >= realvalueForThisBitcount)
- encodedValue = realvalueForThisBitcount - 1;
+ if (remainingBitCount == 0) {
+ // no more bits remaining to encode actual value
- encodedValue = (encodedValue << 1) + negativeBit;
+ if (negativeBit == 0)
+ return range;
+ else
+ return -range;
- return encodedValue;
- }
- }
+ } else {
+ // still one or more bits left, encode value as precisely as
+ // possible
- public static void storeIntegerCompressed8(
- final BitOutputStream outputStream, final int data)
- throws IOException {
+ final int encodedValue = (encodedBits >>> 1) + 1;
- if (data < 256) {
- outputStream.storeBits(0, 1);
- outputStream.storeBits(data, 8);
- } else {
- outputStream.storeBits(1, 1);
- outputStream.storeBits(data, 32);
- }
- }
+ final int realvalueForThisBitcount = 1 << remainingBitCount;
- Image image;
- int width, height;
+ // int valueMultiplier = range / realvalueForThisBitcount;
+ int decodedValue = (range * encodedValue)
+ / realvalueForThisBitcount;
- Channel yChannel;
+ if (decodedValue > range)
+ decodedValue = range;
- Channel uChannel;
- Channel vChannel;
- Approximator approximator;
+ if (negativeBit == 0)
+ return decodedValue;
+ else
+ return -decodedValue;
- int bitsForY;
- int bitsForU;
+ }
+ }
- int bitsForV;
+ private static int encodeValueIntoGivenBits(int value, final int range,
+ final int bitCount) {
- // ColorStats colorStats = new ColorStats();
- OperatingContext context = new OperatingContext();
+ int negativeBit = 0;
- OperatingContext context2 = new OperatingContext();
+ if (value < 0) {
+ negativeBit = 1;
+ value = -value;
+ }
- BitOutputStream bitOutputStream;
+ final int remainingBitCount = bitCount - 1;
- public ImageEncoder(final Image image) {
- approximator = new Approximator();
+ if (remainingBitCount == 0)
+ return negativeBit;
+ else {
+ // still one or more bits left, encode value as precisely as
+ // possible
- // bitOutputStream = outputStream;
+ if (value > range)
+ value = range;
- this.image = image;
+ final int realvalueForThisBitcount = 1 << remainingBitCount;
+ // int valueMultiplier = range / realvalueForThisBitcount;
+ int encodedValue = (value * realvalueForThisBitcount) / range;
- }
+ if (encodedValue >= realvalueForThisBitcount)
+ encodedValue = realvalueForThisBitcount - 1;
- public void encode(final BitOutputStream bitOutputStream)
- throws IOException {
- this.bitOutputStream = bitOutputStream;
+ encodedValue = (encodedValue << 1) + negativeBit;
- approximator.initialize();
+ return encodedValue;
+ }
+ }
- approximator.save(bitOutputStream);
+ public static void storeIntegerCompressed8(
+ final BitOutputStream outputStream, final int data)
+ throws IOException {
- width = image.metaData.width;
- height = image.metaData.height;
+ if (data < 256) {
+ outputStream.storeBits(0, 1);
+ outputStream.storeBits(data, 8);
+ } else {
+ outputStream.storeBits(1, 1);
+ outputStream.storeBits(data, 32);
+ }
+ }
- final WritableRaster raster = image.bufferedImage.getRaster();
- final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
- final byte[] pixels = dbi.getData();
+ public void encode(final BitOutputStream bitOutputStream)
+ throws IOException {
+ this.bitOutputStream = bitOutputStream;
- if (yChannel == null)
- yChannel = new Channel(width, height);
- else
- yChannel.reset();
+ approximator.initialize();
- if (uChannel == null)
- uChannel = new Channel(width, height);
- else
- uChannel.reset();
+ approximator.save(bitOutputStream);
- if (vChannel == null)
- vChannel = new Channel(width, height);
- else
- vChannel.reset();
+ width = image.metaData.width;
+ height = image.metaData.height;
- // create YUV map out of RGB raster data
- final Color color = new Color();
+ final WritableRaster raster = image.bufferedImage.getRaster();
+ final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+ final byte[] pixels = dbi.getData();
- for (int y = 0; y < height; y++)
- for (int x = 0; x < width; x++) {
+ if (yChannel == null)
+ yChannel = new Channel(width, height);
+ else
+ yChannel.reset();
- final int index = (y * width) + x;
- final int colorBufferIndex = index * 3;
+ if (uChannel == null)
+ uChannel = new Channel(width, height);
+ else
+ uChannel.reset();
- int blue = pixels[colorBufferIndex];
- if (blue < 0)
- blue = blue + 256;
+ if (vChannel == null)
+ vChannel = new Channel(width, height);
+ else
+ vChannel.reset();
- int green = pixels[colorBufferIndex + 1];
- if (green < 0)
- green = green + 256;
+ // create YUV map out of RGB raster data
+ final Color color = new Color();
- int red = pixels[colorBufferIndex + 2];
- if (red < 0)
- red = red + 256;
+ for (int y = 0; y < height; y++)
+ for (int x = 0; x < width; x++) {
- color.r = red;
- color.g = green;
- color.b = blue;
+ final int index = (y * width) + x;
+ final int colorBufferIndex = index * 3;
- color.RGB2YUV();
+ int blue = pixels[colorBufferIndex];
+ if (blue < 0)
+ blue = blue + 256;
- yChannel.map[index] = (byte) color.y;
- uChannel.map[index] = (byte) color.u;
- vChannel.map[index] = (byte) color.v;
- }
+ int green = pixels[colorBufferIndex + 1];
+ if (green < 0)
+ green = green + 256;
- yChannel.decodedMap[0] = yChannel.map[0];
- uChannel.decodedMap[0] = uChannel.map[0];
- vChannel.decodedMap[0] = vChannel.map[0];
+ int red = pixels[colorBufferIndex + 2];
+ if (red < 0)
+ red = red + 256;
- bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);
- bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);
- bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);
+ color.r = red;
+ color.g = green;
+ color.b = blue;
- // detect initial step
- int largestDimension;
- int initialStep = 2;
- if (width > height)
- largestDimension = width;
- else
- largestDimension = height;
+ color.RGB2YUV();
- while (initialStep < largestDimension)
- initialStep = initialStep * 2;
+ yChannel.map[index] = (byte) color.y;
+ uChannel.map[index] = (byte) color.u;
+ vChannel.map[index] = (byte) color.v;
+ }
- rangeGrid(initialStep);
- rangeRoundGrid(2);
- saveGrid(initialStep);
- }
+ yChannel.decodedMap[0] = yChannel.map[0];
+ uChannel.decodedMap[0] = uChannel.map[0];
+ vChannel.decodedMap[0] = vChannel.map[0];
- 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 {
+ bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);
+ bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);
+ bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);
- final byte[] decodedRangeMap = channel.decodedRangeMap;
- final byte[] decodedMap = channel.decodedMap;
+ // detect initial step
+ int largestDimension;
+ int initialStep = 2;
+ if (width > height)
+ largestDimension = width;
+ else
+ largestDimension = height;
- final int inheritedRange = byteToInt(decodedRangeMap[parentIndex]);
+ while (initialStep < largestDimension)
+ initialStep = initialStep * 2;
- final int inheritedBitCount = table
- .proposeBitcountForRange(inheritedRange);
+ 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);
- if (inheritedBitCount > 0) {
- int computedRange;
- computedRange = table.proposeRangeForRange(range, inheritedRange);
- decodedRangeMap[index] = (byte) computedRange;
+ 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);
+ 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;
+ 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;
+ decodedMap[index] = (byte) decodedValue;
+ } else
+ decodedMap[index] = (byte) averageDecodedValue;
- } else {
- decodedRangeMap[index] = (byte) inheritedRange;
- decodedMap[index] = (byte) averageDecodedValue;
- }
- }
+ } else {
+ decodedRangeMap[index] = (byte) inheritedRange;
+ decodedMap[index] = (byte) averageDecodedValue;
+ }
+ }
- public void printStatistics() {
- System.out.println("Y channel:");
- yChannel.printStatistics();
+ public void printStatistics() {
+ System.out.println("Y channel:");
+ yChannel.printStatistics();
- System.out.println("U channel:");
- uChannel.printStatistics();
+ System.out.println("U channel:");
+ uChannel.printStatistics();
- System.out.println("V channel:");
- vChannel.printStatistics();
- }
+ System.out.println("V channel:");
+ vChannel.printStatistics();
+ }
- public void rangeGrid(final int step) {
+ private 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);
+ // 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);
- }
+ 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) {
+ 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) {
- final int index = (y * width) + x;
- final int halfStep = step / 2;
+ final int index = (y * width) + x;
+ final int halfStep = step / 2;
- context.initialize(image, yChannel.map, uChannel.map,
- vChannel.map);
+ context.initialize(image, yChannel.map, uChannel.map,
+ vChannel.map);
- context.measureNeighborEncode(x - halfStep, y - halfStep);
- context.measureNeighborEncode(x + halfStep, y - halfStep);
- context.measureNeighborEncode(x - halfStep, y + halfStep);
- context.measureNeighborEncode(x + halfStep, y + halfStep);
+ context.measureNeighborEncode(x - halfStep, y - halfStep);
+ context.measureNeighborEncode(x + halfStep, y - halfStep);
+ context.measureNeighborEncode(x - halfStep, y + halfStep);
+ context.measureNeighborEncode(x + halfStep, y + halfStep);
- yChannel.rangeMap[index] = (byte) context.getYRange(index);
- uChannel.rangeMap[index] = (byte) context.getURange(index);
- vChannel.rangeMap[index] = (byte) context.getVRange(index);
- }
- }
+ yChannel.rangeMap[index] = (byte) context.getYRange(index);
+ 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 x = offsetX; x < width; x = x + step) {
+ 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 index = (y * width) + x;
- final int halfStep = step / 2;
+ final int index = (y * width) + x;
+ final int halfStep = step / 2;
- context.initialize(image, yChannel.map, uChannel.map,
- vChannel.map);
+ context.initialize(image, yChannel.map, uChannel.map,
+ vChannel.map);
- context.measureNeighborEncode(x - halfStep, y);
- context.measureNeighborEncode(x + halfStep, y);
- context.measureNeighborEncode(x, y - halfStep);
- context.measureNeighborEncode(x, y + halfStep);
+ context.measureNeighborEncode(x - halfStep, y);
+ context.measureNeighborEncode(x + halfStep, y);
+ context.measureNeighborEncode(x, y - halfStep);
+ context.measureNeighborEncode(x, y + halfStep);
- yChannel.rangeMap[index] = (byte) context.getYRange(index);
- uChannel.rangeMap[index] = (byte) context.getURange(index);
- vChannel.rangeMap[index] = (byte) context.getVRange(index);
- }
- }
+ yChannel.rangeMap[index] = (byte) context.getYRange(index);
+ uChannel.rangeMap[index] = (byte) context.getURange(index);
+ vChannel.rangeMap[index] = (byte) context.getVRange(index);
+ }
+ }
- public void rangeRoundGrid(final int step) {
+ private void rangeRoundGrid(final int step) {
- rangeRoundGridDiagonal(step / 2, step / 2, step);
- rangeRoundGridSquare(step / 2, 0, step);
- rangeRoundGridSquare(0, step / 2, step);
+ rangeRoundGridDiagonal(step / 2, step / 2, step);
+ rangeRoundGridSquare(step / 2, 0, step);
+ rangeRoundGridSquare(0, step / 2, step);
- if (step < 1024)
- rangeRoundGrid(step * 2);
- }
+ 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 x = offsetX; x < width; x = x + 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) {
- final int index = (y * width) + x;
+ final int index = (y * width) + x;
- 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;
- final int parentIndex = ((y - halfStep) * width)
- + (x - halfStep);
+ final int parentIndex = ((y - halfStep) * width)
+ + (x - halfStep);
- int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
+ int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
- if (parentYRange < yRange) {
- parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte) parentYRange;
- }
+ if (parentYRange < yRange) {
+ parentYRange = yRange;
+ yChannel.rangeMap[parentIndex] = (byte) parentYRange;
+ }
- int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
+ int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
- if (parentURange < uRange) {
- parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte) parentURange;
- }
+ if (parentURange < uRange) {
+ parentURange = uRange;
+ uChannel.rangeMap[parentIndex] = (byte) parentURange;
+ }
- int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
+ int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
- if (parentVRange < vRange) {
- parentVRange = vRange;
- vChannel.rangeMap[parentIndex] = (byte) parentVRange;
- }
- }
- }
+ if (parentVRange < vRange) {
+ parentVRange = vRange;
+ 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 x = offsetX; x < width; x = x + step) {
+ 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 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)
+ parentIndex = (y * width) + (x - halfStep);
+ else
+ parentIndex = ((y - halfStep) * width) + x;
- int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
+ int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
- if (parentYRange < yRange) {
- parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte) parentYRange;
- }
+ if (parentYRange < yRange) {
+ parentYRange = yRange;
+ yChannel.rangeMap[parentIndex] = (byte) parentYRange;
+ }
- int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
+ int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
- if (parentURange < uRange) {
- parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte) parentURange;
- }
+ if (parentURange < uRange) {
+ parentURange = uRange;
+ uChannel.rangeMap[parentIndex] = (byte) parentURange;
+ }
- int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
+ int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
- if (parentVRange < vRange) {
- parentVRange = vRange;
- vChannel.rangeMap[parentIndex] = (byte) parentVRange;
- }
+ if (parentVRange < vRange) {
+ parentVRange = vRange;
+ vChannel.rangeMap[parentIndex] = (byte) parentVRange;
+ }
- }
- }
+ }
+ }
- public void saveGrid(final int step) throws IOException {
+ private void saveGrid(final int step) throws IOException {
- saveGridDiagonal(step / 2, step / 2, step);
- saveGridSquare(step / 2, 0, step);
- saveGridSquare(0, step / 2, step);
+ saveGridDiagonal(step / 2, step / 2, step);
+ saveGridSquare(step / 2, 0, step);
+ saveGridSquare(0, step / 2, step);
- if (step > 2)
- saveGrid(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 x = offsetX; x < width; x = x + step) {
+ 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 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);
+ 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);
- savePixel(step, offsetX, offsetY, x, y,
- context2.colorStats.getAverageY(),
- context2.colorStats.getAverageU(),
- context2.colorStats.getAverageV());
+ savePixel(step, offsetX, offsetY, x, y,
+ context2.colorStats.getAverageY(),
+ context2.colorStats.getAverageU(),
+ 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 x = offsetX; x < width; x = x + step) {
+ 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;
+ 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);
+ 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);
- savePixel(step, offsetX, offsetY, x, y,
- context2.colorStats.getAverageY(),
- context2.colorStats.getAverageU(),
- context2.colorStats.getAverageV());
+ savePixel(step, offsetX, offsetY, x, y,
+ context2.colorStats.getAverageY(),
+ context2.colorStats.getAverageU(),
+ context2.colorStats.getAverageV());
- }
- }
+ }
+ }
- 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 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 {
- 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 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) {
- 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 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;
- encodeChannel(approximator.yTable, yChannel, averageDecodedY, index,
- py, yRange, parentIndex);
-
- encodeChannel(approximator.uTable, uChannel, averageDecodedU, index,
- pu, uRange, parentIndex);
+ encodeChannel(approximator.yTable, yChannel, averageDecodedY, index,
+ py, yRange, parentIndex);
+
+ encodeChannel(approximator.uTable, uChannel, averageDecodedU, index,
+ pu, uRange, parentIndex);
- encodeChannel(approximator.vTable, vChannel, averageDecodedV, index,
- pv, vRange, parentIndex);
+ encodeChannel(approximator.vTable, vChannel, averageDecodedV, index,
+ pv, vRange, parentIndex);
- }
+ }
}
* Like image dimensions, header version, compression quality, etc..
*/
-import java.io.IOException;
-
import eu.svjatoslav.commons.data.BitInputStream;
import eu.svjatoslav.commons.data.BitOutputStream;
+import java.io.IOException;
+
public class ImageMetaData {
- int version;
- int width;
- int height;
-
- public void load(final BitInputStream inputStream) throws IOException {
- version = inputStream.readBits(16);
- width = ImageDecoder.readIntegerCompressed8(inputStream);
- height = ImageDecoder.readIntegerCompressed8(inputStream);
- }
-
- public void save(final BitOutputStream outputStream) throws IOException {
- outputStream.storeBits(version, 16);
- ImageEncoder.storeIntegerCompressed8(outputStream, width);
- ImageEncoder.storeIntegerCompressed8(outputStream, height);
- }
+ int version;
+ int width;
+ int height;
+
+ public void load(final BitInputStream inputStream) throws IOException {
+ version = inputStream.readBits(16);
+ width = ImageDecoder.readIntegerCompressed8(inputStream);
+ height = ImageDecoder.readIntegerCompressed8(inputStream);
+ }
+
+ public void save(final BitOutputStream outputStream) throws IOException {
+ outputStream.storeBits(version, 16);
+ ImageEncoder.storeIntegerCompressed8(outputStream, width);
+ ImageEncoder.storeIntegerCompressed8(outputStream, height);
+ }
}
package eu.svjatoslav.imagesqueeze.codec;
-public class OperatingContext {
+class OperatingContext {
- Image image;
- byte[] yMap;
- byte[] uMap;
- byte[] vMap;
- ColorStats colorStats = new ColorStats();
+ final ColorStats colorStats = new ColorStats();
+ private Image image;
+ private byte[] yMap;
+ private byte[] uMap;
+ private byte[] vMap;
- public OperatingContext() {
- }
+ public OperatingContext() {
+ }
- public int getURange(final int index) {
- final int colorness = ImageEncoder.byteToInt(uMap[index]);
- return Math.abs(colorness - colorStats.getAverageU());
- }
+ public int getURange(final int index) {
+ final int colorness = ImageEncoder.byteToInt(uMap[index]);
+ return Math.abs(colorness - colorStats.getAverageU());
+ }
- public int getVRange(final int index) {
- final int color = ImageEncoder.byteToInt(vMap[index]);
- return Math.abs(color - colorStats.getAverageV());
- }
+ public int getVRange(final int index) {
+ final int color = ImageEncoder.byteToInt(vMap[index]);
+ return Math.abs(color - colorStats.getAverageV());
+ }
- public int getYRange(final int index) {
- final int brightness = ImageEncoder.byteToInt(yMap[index]);
- return Math.abs(brightness - colorStats.getAverageY());
- }
+ public int getYRange(final int index) {
+ final int brightness = ImageEncoder.byteToInt(yMap[index]);
+ return Math.abs(brightness - colorStats.getAverageY());
+ }
- public void initialize(final Image image, final byte[] brightnessMap,
- final byte[] colornessMap, final byte[] colorMap) {
- this.image = image;
- yMap = brightnessMap;
- uMap = colornessMap;
- vMap = colorMap;
+ public void initialize(final Image image, final byte[] brightnessMap,
+ final byte[] colornessMap, final byte[] colorMap) {
+ this.image = image;
+ yMap = brightnessMap;
+ uMap = colornessMap;
+ vMap = colorMap;
- colorStats.reset();
- }
+ colorStats.reset();
+ }
- public void measureNeighborEncode(final int x, final int y) {
- if ((y >= 0) && (y < image.metaData.height) && (x >= 0)
- && (x < image.metaData.width)) {
+ public void measureNeighborEncode(final int x, final int y) {
+ if ((y >= 0) && (y < image.metaData.height) && (x >= 0)
+ && (x < image.metaData.width)) {
- final int neighborIndex = (y * image.metaData.width) + x;
+ final int neighborIndex = (y * image.metaData.width) + x;
- colorStats.ySum = colorStats.ySum
- + ImageEncoder.byteToInt(yMap[neighborIndex]);
- colorStats.uSum = colorStats.uSum
- + ImageEncoder.byteToInt(uMap[neighborIndex]);
- colorStats.vSum = colorStats.vSum
- + ImageEncoder.byteToInt(vMap[neighborIndex]);
- colorStats.pixelCount++;
- }
- }
+ colorStats.ySum = colorStats.ySum
+ + ImageEncoder.byteToInt(yMap[neighborIndex]);
+ colorStats.uSum = colorStats.uSum
+ + ImageEncoder.byteToInt(uMap[neighborIndex]);
+ colorStats.vSum = colorStats.vSum
+ + ImageEncoder.byteToInt(vMap[neighborIndex]);
+ colorStats.pixelCount++;
+ }
+ }
}
package eu.svjatoslav.imagesqueeze.codec;
-import java.io.IOException;
-
import eu.svjatoslav.commons.data.BitInputStream;
import eu.svjatoslav.commons.data.BitOutputStream;
+import java.io.IOException;
+
/**
* Quick lookup table.
*/
-public class Table implements Comparable<Table> {
-
- int[] range = new int[100];
- int[] switchTreshold = new int[100];
- int[] bitcount = new int[100];
-
- int[] bitCountForRange = new int[256];
- int[] proposedRangeForActualRange = new int[256];
- int[] proposedRangeForActualRangeLow = new int[256];
- int[] proposedRangeForActualRangeHigh = new int[256];
- byte[] proposedDecreasedRange = new byte[256];
-
- int usedEntries = 0;
-
- /**
- * @param switchTreshold
- * - switch to this range when actual range in equal or below
- * this treshold
- */
- public void addEntry(int range, int switchTreshold, int bitcount) {
- if (range < 0)
- range = 0;
- if (range > 255)
- range = 255;
-
- if (switchTreshold < 0)
- switchTreshold = 0;
- if (switchTreshold > 255)
- switchTreshold = 255;
-
- if (bitcount < 0)
- bitcount = 0;
- if (bitcount > 8)
- bitcount = 8;
-
- this.range[usedEntries] = range;
- this.switchTreshold[usedEntries] = switchTreshold;
- this.bitcount[usedEntries] = bitcount;
- usedEntries++;
- }
-
- /**
- * Compares two tables. Ignores table initialization.
- */
- @Override
- public int compareTo(final Table o) {
- if (usedEntries < o.usedEntries)
- return -1;
- if (usedEntries > o.usedEntries)
- return 1;
-
- for (int i = 0; i < usedEntries; i++) {
- if (range[i] < o.range[i])
- return -1;
- if (range[i] > o.range[i])
- return 1;
-
- if (switchTreshold[i] < o.switchTreshold[i])
- return -1;
- if (switchTreshold[i] > o.switchTreshold[i])
- return 1;
-
- if (bitcount[i] < o.bitcount[i])
- return -1;
- if (bitcount[i] > o.bitcount[i])
- return 1;
- }
-
- return 0;
- }
-
- public void computeLookupTables() {
- int currentCheckPointer = 0;
-
- for (int i = 0; i < 256; i++) {
-
- if (range[currentCheckPointer] == i)
- currentCheckPointer++;
-
- if (currentCheckPointer > 0)
- bitCountForRange[i] = bitcount[currentCheckPointer - 1];
- else
- bitCountForRange[i] = 0;
-
- }
-
- for (int i = 0; i < 256; i++) {
-
- int seek;
- seekLoop: {
- for (seek = 0; seek < usedEntries; seek++)
- if (switchTreshold[seek] >= i)
- break seekLoop;
- }
-
- proposedRangeForActualRange[i] = range[seek];
- if (seek == 0)
- proposedRangeForActualRangeLow[i] = 0;
- else
- proposedRangeForActualRangeLow[i] = switchTreshold[seek - 1] + 1;
- proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
- }
-
- currentCheckPointer = usedEntries - 2;
- for (int i = 255; i >= 0; i--) {
- if (range[currentCheckPointer] == i)
- currentCheckPointer--;
-
- if (currentCheckPointer < 0)
- proposedDecreasedRange[i] = 0;
- else
- proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
- }
-
- }
-
- public void load(final BitInputStream inputStream) throws IOException {
- reset();
-
- final int availableEntries = ImageDecoder
- .readIntegerCompressed8(inputStream);
-
- for (int i = 0; i < availableEntries; i++)
- addEntry(inputStream.readBits(8), inputStream.readBits(8),
- inputStream.readBits(4));
- }
-
- public int proposeBitcountForRange(int range) {
- if (range > 255)
- range = 255;
- if (range < 0)
- range = 0;
- final int proposal = bitCountForRange[range];
- return proposal;
- }
-
- public int proposeDecreasedRange(int range) {
- if (range > 255)
- range = 255;
- if (range < 0)
- range = 0;
-
- return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
- }
-
- public int proposeRangeForRange(final int actualRange, int inheritedRange) {
-
- if (inheritedRange > 255)
- inheritedRange = 255;
- if (inheritedRange < 0)
- inheritedRange = 0;
-
- if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
- return inheritedRange;
-
- return proposeDecreasedRange(inheritedRange);
- }
-
- public void reset() {
- range = new int[100];
- switchTreshold = new int[100];
- bitcount = new int[100];
-
- bitCountForRange = new int[256];
- proposedRangeForActualRange = new int[256];
- proposedRangeForActualRangeLow = new int[256];
- proposedRangeForActualRangeHigh = new int[256];
- proposedDecreasedRange = new byte[256];
-
- usedEntries = 0;
- }
-
- public void save(final BitOutputStream outputStream) throws IOException {
- ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
-
- for (int i = 0; i < usedEntries; i++) {
- outputStream.storeBits(range[i], 8);
- outputStream.storeBits(switchTreshold[i], 8);
- outputStream.storeBits(bitcount[i], 4);
- }
- }
+public class Table {
+
+ private int[] range = new int[100];
+ private int[] switchThreshold = new int[100];
+ private int[] bitCount = new int[100];
+
+ private int[] bitCountForRange = new int[256];
+ private int[] proposedRangeForActualRange = new int[256];
+ private int[] proposedRangeForActualRangeLow = new int[256];
+ private int[] proposedRangeForActualRangeHigh = new int[256];
+ private byte[] proposedDecreasedRange = new byte[256];
+
+ private int usedEntries = 0;
+
+ /**
+ * @param switchThreshold - switch to this range when actual range in equal or below
+ * this threshold
+ */
+ public void addEntry(int range, int switchThreshold, int bitcount) {
+ if (range < 0)
+ range = 0;
+ if (range > 255)
+ range = 255;
+
+ if (switchThreshold < 0)
+ switchThreshold = 0;
+ if (switchThreshold > 255)
+ switchThreshold = 255;
+
+ if (bitcount < 0)
+ bitcount = 0;
+ if (bitcount > 8)
+ bitcount = 8;
+
+ this.range[usedEntries] = range;
+ this.switchThreshold[usedEntries] = switchThreshold;
+ this.bitCount[usedEntries] = bitcount;
+ usedEntries++;
+ }
+
+ public void computeLookupTables() {
+ int currentCheckPointer = 0;
+
+ for (int i = 0; i < 256; i++) {
+
+ if (range[currentCheckPointer] == i)
+ currentCheckPointer++;
+
+ if (currentCheckPointer > 0)
+ bitCountForRange[i] = bitCount[currentCheckPointer - 1];
+ else
+ bitCountForRange[i] = 0;
+
+ }
+
+ for (int i = 0; i < 256; i++) {
+
+ int seek;
+ seekLoop:
+ {
+ for (seek = 0; seek < usedEntries; seek++)
+ if (switchThreshold[seek] >= i)
+ break seekLoop;
+ }
+
+ proposedRangeForActualRange[i] = range[seek];
+ if (seek == 0)
+ proposedRangeForActualRangeLow[i] = 0;
+ else
+ proposedRangeForActualRangeLow[i] = switchThreshold[seek - 1] + 1;
+ proposedRangeForActualRangeHigh[i] = switchThreshold[seek];
+ }
+
+ currentCheckPointer = usedEntries - 2;
+ for (int i = 255; i >= 0; i--) {
+ if (range[currentCheckPointer] == i)
+ currentCheckPointer--;
+
+ if (currentCheckPointer < 0)
+ proposedDecreasedRange[i] = 0;
+ else
+ proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
+ }
+
+ }
+
+ public void load(final BitInputStream inputStream) throws IOException {
+ reset();
+
+ final int availableEntries = ImageDecoder
+ .readIntegerCompressed8(inputStream);
+
+ for (int i = 0; i < availableEntries; i++)
+ addEntry(inputStream.readBits(8), inputStream.readBits(8),
+ inputStream.readBits(4));
+ }
+
+ public int proposeBitcountForRange(int range) {
+ if (range > 255)
+ range = 255;
+ if (range < 0)
+ range = 0;
+ return bitCountForRange[range];
+ }
+
+ public int proposeDecreasedRange(int range) {
+ if (range > 255)
+ range = 255;
+ if (range < 0)
+ range = 0;
+
+ return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
+ }
+
+ public int proposeRangeForRange(final int actualRange, int inheritedRange) {
+
+ if (inheritedRange > 255)
+ inheritedRange = 255;
+ if (inheritedRange < 0)
+ inheritedRange = 0;
+
+ if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange)
+ return inheritedRange;
+
+ return proposeDecreasedRange(inheritedRange);
+ }
+
+ public void reset() {
+ range = new int[100];
+ switchThreshold = new int[100];
+ bitCount = new int[100];
+
+ bitCountForRange = new int[256];
+ proposedRangeForActualRange = new int[256];
+ proposedRangeForActualRangeLow = new int[256];
+ proposedRangeForActualRangeHigh = new int[256];
+ proposedDecreasedRange = new byte[256];
+
+ usedEntries = 0;
+ }
+
+ public void save(final BitOutputStream outputStream) throws IOException {
+ ImageEncoder.storeIntegerCompressed8(outputStream, usedEntries);
+
+ for (int i = 0; i < usedEntries; i++) {
+ outputStream.storeBits(range[i], 8);
+ outputStream.storeBits(switchThreshold[i], 8);
+ outputStream.storeBits(bitCount[i], 4);
+ }
+ }
}
package eu.svjatoslav.imagesqueeze.sampleApplication;
-import java.awt.BorderLayout;
-
-import javax.swing.SwingUtilities;
-import javax.swing.WindowConstants;
+import javax.swing.*;
+import java.awt.*;
public class ImageFrame extends javax.swing.JFrame {
- private ImagePanel imagePanel1;
-
- public ImageFrame(final String title) {
- super();
- setTitle(title);
- initGUI();
- }
-
- public ImagePanel getImagePanel() {
- return imagePanel1;
- }
-
- private void initGUI() {
- try {
- final BorderLayout thisLayout = new BorderLayout();
- setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- getContentPane().setLayout(thisLayout);
- {
- imagePanel1 = new ImagePanel();
- getContentPane().add(getImagePanel(), BorderLayout.CENTER);
- }
- pack();
- } catch (final Exception e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Auto-generated main method to display this JFrame
- */
- public static void main(final String[] args) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- final ImageFrame inst = new ImageFrame("test");
- inst.setLocationRelativeTo(null);
- inst.setVisible(true);
- }
- });
- }
+ private ImagePanel imagePanel1;
+
+ public ImageFrame(final String title) {
+ super();
+ setTitle(title);
+ initGUI();
+ }
+
+ /**
+ * Auto-generated main method to display this JFrame
+ */
+ public static void main(final String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ final ImageFrame inst = new ImageFrame("test");
+ inst.setLocationRelativeTo(null);
+ inst.setVisible(true);
+ });
+ }
+
+ public ImagePanel getImagePanel() {
+ return imagePanel1;
+ }
+
+ private void initGUI() {
+ try {
+ final BorderLayout thisLayout = new BorderLayout();
+ setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ getContentPane().setLayout(thisLayout);
+ {
+ imagePanel1 = new ImagePanel();
+ getContentPane().add(getImagePanel(), BorderLayout.CENTER);
+ }
+ pack();
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
}
package eu.svjatoslav.imagesqueeze.sampleApplication;
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import javax.swing.ImageIcon;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.WindowConstants;
-
import eu.svjatoslav.imagesqueeze.codec.Image;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.*;
+
public class ImagePanel extends javax.swing.JPanel {
- private JLabel imageLabel;
-
- public BufferedImage bufferedImage;
-
- public ImagePanel() {
- super();
- initGUI();
- }
-
- public void createEmptyImage(final Dimension dimension) {
-
- bufferedImage = new BufferedImage(dimension.width, dimension.height,
- BufferedImage.TYPE_3BYTE_BGR);
-
- final ImageIcon icon = new ImageIcon(bufferedImage);
-
- imageLabel.setIcon(icon);
- }
-
- public JLabel getImageLabel() {
- return imageLabel;
- }
-
- private void initGUI() {
- try {
- final BorderLayout thisLayout = new BorderLayout();
- setLayout(thisLayout);
- setPreferredSize(new Dimension(660, 500));
- {
- imageLabel = new JLabel();
- this.add(getImageLabel(), BorderLayout.CENTER);
- }
- } catch (final Exception e) {
- e.printStackTrace();
- }
- }
-
- public void loadImage(final File inputFile, final boolean isImgSqz)
- throws IOException {
- final FileInputStream fileInputStream = new FileInputStream(inputFile);
-
- loadImage(fileInputStream, isImgSqz);
- }
-
- public void loadImage(final InputStream inputStream, final boolean isImgSqz)
- throws IOException {
- if (isImgSqz) {
- // load ImageSqueeze file
-
- final Image image = new Image();
- image.loadImage(inputStream);
-
- bufferedImage = image.bufferedImage;
-
- final ImageIcon icon = new ImageIcon(bufferedImage);
- // ImageIcon icon = new ImageIcon("sample data/original.png");
-
- imageLabel.setIcon(icon);
-
- } else {
- // load JPEG, PNG, GIF file
- final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- readLoop: {
- for (;;) {
- final int b = inputStream.read();
- if (b == -1)
- break readLoop;
- outputStream.write(b);
- }
- }
-
- final ImageIcon icon = new ImageIcon(outputStream.toByteArray());
-
- bufferedImage = new BufferedImage(icon.getIconWidth(),
- icon.getIconHeight(), BufferedImage.TYPE_3BYTE_BGR);
- bufferedImage.getGraphics().drawImage(icon.getImage(), 0, 0, null);
-
- final ImageIcon displayIcon = new ImageIcon(bufferedImage);
- imageLabel.setIcon(displayIcon);
- }
- }
-
- public void saveImage(final File outputFile) {
- final Image image = new Image(bufferedImage);
- try {
- image.saveImage(outputFile);
- } catch (final Exception e) {
- System.out.println("Error while saving image: " + e.toString());
- }
- }
-
- /**
- * Auto-generated main method to display this JPanel inside a new JFrame.
- */
- public static void main(final String[] args) {
- final JFrame frame = new JFrame();
- frame.getContentPane().add(new ImagePanel());
- frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- frame.pack();
- frame.setVisible(true);
- }
+ private BufferedImage bufferedImage;
+ private JLabel imageLabel;
+
+ public ImagePanel() {
+ super();
+ initGUI();
+ }
+
+ /**
+ * Auto-generated main method to display this JPanel inside a new JFrame.
+ */
+ public static void main(final String[] args) {
+ final JFrame frame = new JFrame();
+ frame.getContentPane().add(new ImagePanel());
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ public void createEmptyImage(final Dimension dimension) {
+
+ bufferedImage = new BufferedImage(dimension.width, dimension.height,
+ BufferedImage.TYPE_3BYTE_BGR);
+
+ final ImageIcon icon = new ImageIcon(bufferedImage);
+
+ imageLabel.setIcon(icon);
+ }
+
+ private JLabel getImageLabel() {
+ return imageLabel;
+ }
+
+ private void initGUI() {
+ try {
+ final BorderLayout thisLayout = new BorderLayout();
+ setLayout(thisLayout);
+ setPreferredSize(new Dimension(660, 500));
+ {
+ imageLabel = new JLabel();
+ this.add(getImageLabel(), BorderLayout.CENTER);
+ }
+ } catch (final Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void loadImage(final File inputFile, final boolean isImgSqz)
+ throws IOException {
+
+ try (final FileInputStream fileInputStream = new FileInputStream(inputFile)) {
+ loadImage(fileInputStream, isImgSqz);
+ }
+ }
+
+ public void loadImage(final InputStream inputStream, final boolean isImgSqz)
+ throws IOException {
+ if (isImgSqz) {
+ // load ImageSqueeze file
+
+ final Image image = new Image();
+ image.loadImage(inputStream);
+
+ bufferedImage = image.bufferedImage;
+
+ final ImageIcon icon = new ImageIcon(bufferedImage);
+ // ImageIcon icon = new ImageIcon("sample data/original.png");
+
+ imageLabel.setIcon(icon);
+
+ } else {
+ // load JPEG, PNG, GIF file
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ readLoop:
+ {
+ for (; ; ) {
+ final int b = inputStream.read();
+ if (b == -1)
+ break readLoop;
+ outputStream.write(b);
+ }
+ }
+
+ final ImageIcon icon = new ImageIcon(outputStream.toByteArray());
+
+ bufferedImage = new BufferedImage(icon.getIconWidth(),
+ icon.getIconHeight(), BufferedImage.TYPE_3BYTE_BGR);
+ bufferedImage.getGraphics().drawImage(icon.getImage(), 0, 0, null);
+
+ final ImageIcon displayIcon = new ImageIcon(bufferedImage);
+ imageLabel.setIcon(displayIcon);
+ }
+ }
+
+ public void saveImage(final File outputFile) {
+ final Image image = new Image(bufferedImage);
+ try {
+ image.saveImage(outputFile);
+ } catch (final Exception e) {
+ System.out.println("Error while saving image: " + e.toString());
+ }
+ }
}
public class Main {
- public static void main(final String[] args) {
+ public static void main(final String[] args) throws IOException {
- try {
+ final String image = "colorful photo";
+ final String sourceDirectory = "/eu/svjatoslav/imagesqueeze/sampleApplication/data/";
- final String image = "colorful photo";
- final String sourceDirectory = "/eu/svjatoslav/imagesqueeze/sampleApplication/data/";
+ // create visible frame
+ // load image into frame
+ final InputStream inputStream = Main.class
+ .getResourceAsStream(sourceDirectory + image + ".png");
- // create visible frame
- // load image into frame
- final InputStream inputStream = Main.class
- .getResourceAsStream(sourceDirectory + image + ".png");
+ final ImageFrame frame = new ImageFrame("Original image");
+ frame.getImagePanel().loadImage(inputStream, false);
+ frame.setVisible(true);
- final ImageFrame frame = new ImageFrame("Original image");
- frame.getImagePanel().loadImage(inputStream, false);
- frame.setVisible(true);
+ // encode image into file
+ frame.getImagePanel().saveImage(new File(image + ".ImgSqz"));
- // encode image into file
- frame.getImagePanel().saveImage(new File(image + ".ImgSqz"));
+ // create second frame for decoded image
+ final ImageFrame frame2 = new ImageFrame("Encoded -> Decoded");
- // create second frame for decoded image
- final ImageFrame frame2 = new ImageFrame("Encoded -> Decoded");
+ // decode image
+ frame2.getImagePanel().loadImage(new File(image + ".ImgSqz"), true);
+ frame2.setVisible(true);
- // decode image
- frame2.getImagePanel().loadImage(new File(image + ".ImgSqz"), true);
- frame2.setVisible(true);
-
- } catch (final IOException exception) {
- System.out.println("Error while loading an image: " + exception);
- }
-
- }
+ }
}