Added license and copyright notice to every source file.
+<?xml version="1.0" encoding="UTF-8"?>
<classpath>
- <classpathentry kind="src" path="src/main/java" including="**/*.java"/>
- <classpathentry kind="output" path="target/classes"/>
- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
-</classpath>
\ No newline at end of file
+ <classpathentry kind="src" output="target/classes" path="src/main/java"/>
+ <classpathentry excluding="**" including="**/*.java" kind="src" output="target/classes" path="src/main/resources"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="target/classes"/>
+</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
- <name>imagesqueeze</name>
- <comment>ImageSqueeze - image codec optimized for photos. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment>
- <projects/>
- <buildSpec>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- </buildCommand>
- <buildCommand>
- <name>org.maven.ide.eclipse.maven2Builder</name>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>org.maven.ide.eclipse.maven2Nature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
\ No newline at end of file
+ <name>imagesqueeze</name>
+ <comment>ImageSqueeze - image codec optimized for photos. NO_M2ECLIPSE_SUPPORT: Project files created with the maven-eclipse-plugin are not supported in M2Eclipse.</comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.maven.ide.eclipse.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.m2e.core.maven2Builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.m2e.core.maven2Nature</nature>
+ <nature>org.maven.ide.eclipse.maven2Nature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
--- /dev/null
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
-#Sun Aug 28 02:19:45 EEST 2011
+eclipse.preferences.version=1
encoding//src/main/java=UTF-8
-org.eclipse.jdt.core.compiler.compliance=1.6
encoding//src/main/resources=UTF-8
-encoding//src/test/resources=UTF-8
encoding//src/test/java=UTF-8
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+encoding//src/test/resources=UTF-8
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.6
--- /dev/null
+activeProfiles=
+eclipse.preferences.version=1
+resolveWorkspaceProjects=false
+version=1
+++ /dev/null
-Imagesqueeze -- Lossy image codec. Optimized for photos and speed.
-By Svjatoslav Agejenko, svjatoslavagejenko@gmail.com, http://svjatoslav.eu
-
-See doc directory for more info.
-
-Todo:
- * Javadoc
-
- * micro header possibility, 16 bits header version, 2x8 bits, image dimension
-
- * Performance test
-
\ No newline at end of file
--- /dev/null
+better documentation describing the idea
+
+JavaDoc
+
+possibility for smaller image header, on smaller images
+
+runnable performance tests
-<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">
- <modelVersion>4.0.0</modelVersion>
- <groupId>eu.svjatoslav</groupId>
- <artifactId>imagesqueeze</artifactId>
- <version>1.0-SNAPSHOT</version>
- <name>imagesqueeze</name>
- <packaging>jar</packaging>
+<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">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>imagesqueeze</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <name>imagesqueeze</name>
+ <packaging>jar</packaging>
- <description>ImageSqueeze - image codec optimized for photos</description>
+ <description>ImageSqueeze - image codec optimized for photos</description>
- <build>
- <finalName>imagesqueeze</finalName>
- <resources>
- <resource>
- <filtering>false</filtering>
- <directory>src/main/resources</directory>
- </resource>
- <resource>
- <filtering>true</filtering>
- <directory>src/main/resources</directory>
- <includes>
- <include>*.xml</include>
- </includes>
- </resource>
- </resources>
- <testResources>
- <testResource>
- <filtering>true</filtering>
- <directory>src/test/resources</directory>
- </testResource>
- </testResources>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <version>2.3.1</version>
- <configuration>
- <source>1.6</source>
- <target>1.6</target>
- <optimize>true</optimize>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-resources-plugin</artifactId>
- <version>2.4.3</version>
- <configuration>
- <encoding>UTF-8</encoding>
- </configuration>
- </plugin>
- </plugins>
- </build>
+ <build>
+ <finalName>imagesqueeze</finalName>
+ <resources>
+ <resource>
+ <filtering>false</filtering>
+ <directory>src/main/resources</directory>
+ </resource>
+ <resource>
+ <filtering>true</filtering>
+ <directory>src/main/resources</directory>
+ <includes>
+ <include>*.xml</include>
+ </includes>
+ </resource>
+ </resources>
+ <testResources>
+ <testResource>
+ <filtering>true</filtering>
+ <directory>src/test/resources</directory>
+ </testResource>
+ </testResources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.1</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ <optimize>true</optimize>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.4.3</version>
+ <configuration>
+ <encoding>UTF-8</encoding>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>eu.svjatoslav</groupId>
+ <artifactId>svjatoslavcommons</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+ <repositories>
+ <repository>
+ <id>svjatoslav.eu</id>
+ <name>Svjatoslav repository</name>
+ <url>http://svjatoslav.eu/static/maven/</url>
+ </repository>
+ </repositories>
- <repositories>
-
- <repository>
- <id>svjatoslav.eu</id>
- <name>Svjatoslav repository</name>
- <url>http://svjatoslav.eu/maven/</url>
- </repository>
-
- </repositories>
-
-
-
- <distributionManagement>
- <!-- use the following if you're not using a snapshot version. <repository> <id>repo</id> <name>Repository Name</name>
- <url>scp://host/path/to/repo</url> </repository> -->
-
- <!-- use the following if you ARE using a snapshot version. -->
- <snapshotRepository>
- <id>svjatoslav.eu</id>
- <name>svjatoslav.eu</name>
- <url>scp://svjatoslav.eu/opt/svjatoslav.eu/webcontent/maven</url>
- </snapshotRepository>
- </distributionManagement>
+ <distributionManagement>
+ <snapshotRepository>
+ <id>svjatoslav.eu</id>
+ <name>svjatoslav.eu</name>
+ <url>scp://svjatoslav.eu/var/lib/tomcat6/_svjatoslav.eu/static/maven</url>
+ </snapshotRepository>
+ </distributionManagement>
</project>
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitInputStream;
+import eu.svjatoslav.commons.data.BitOutputStream;
+
/**
- * Since it is a lossy codec, instead of storing
- * exact values, approximated values are stored
- * to save on bit count.
+ * 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 uTable = new Table();
public Table vTable = new Table();
- public Approximator(){
+ public Approximator() {
}
- public int compareTo(Approximator o) {
+ @Override
+ public int compareTo(final Approximator o) {
int result = yTable.compareTo(o.yTable);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
result = uTable.compareTo(o.uTable);
- if (result != 0) return result;
+ if (result != 0)
+ return result;
result = vTable.compareTo(o.vTable);
return result;
}
-
- public void initialize(){
+
+ 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 save(BitOutputStream outputStream) throws IOException{
- yTable.save(outputStream);
- uTable.save(outputStream);
- vTable.save(outputStream);
- }
-
- public void load(BitInputStream inputStream) throws IOException {
+
+ public void load(final BitInputStream inputStream) throws IOException {
yTable.load(inputStream);
uTable.load(inputStream);
- vTable.load(inputStream);
+ vTable.load(inputStream);
}
- public void computeLookupTables(){
- yTable.computeLookupTables();
- uTable.computeLookupTables();
- vTable.computeLookupTables();
+ public void save(final BitOutputStream outputStream) throws IOException {
+ yTable.save(outputStream);
+ uTable.save(outputStream);
+ vTable.save(outputStream);
}
-
+
}
+++ /dev/null
-package eu.svjatoslav.imagesqueeze.codec;
-
-/**
- * Read individual bits from the input stream.
- */
-
-import java.io.IOException;
-import java.io.InputStream;
-
-public class BitInputStream {
-
-
- int currentByte;
- int currentBytePointer = -1;
-
- InputStream inputStream;
-
- public BitInputStream(InputStream inputStream){
- this.inputStream = inputStream;
- }
-
- public int readBits(int bitCount) throws IOException {
-
- int readableByte = 0;
- for (int i=0; i < bitCount; i++){
-
- readableByte = readableByte << 1;
-
- if (currentBytePointer == -1){
- currentBytePointer = 7;
- currentByte = inputStream.read();
- }
-
- int mask = 1;
- mask = mask << currentBytePointer;
-
- int currentBit = currentByte & mask;
-
- if (currentBit != 0){
- readableByte = readableByte | 1;
- }
-
- currentBytePointer--;
- }
- return readableByte;
- }
-
- public int readIntegerCompressed8() throws IOException{
- if (readBits(1) == 0){
- return readBits(8);
- } else {
- return readBits(32);
- }
- }
-
-}
+++ /dev/null
-package eu.svjatoslav.imagesqueeze.codec;
-
-/**
- * Write individual bits to the output stream.
- */
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class BitOutputStream {
-
- int currentByte;
- int currentBytePointer;
-
- OutputStream outputStream;
-
- public BitOutputStream(OutputStream outputStream){
- currentByte = 0;
- currentBytePointer = 0;
- this.outputStream = outputStream;
- };
-
-
- public void storeBits(int data, int bitCount) throws IOException {
- for (int i=bitCount-1; i >= 0; i--){
-
- int mask = 1;
- mask = mask << i;
-
- int currentBit = data & mask;
- currentByte = currentByte << 1;
-
- if (currentBit != 0){
- currentByte = currentByte | 1;
- }
- currentBytePointer++;
-
- if (currentBytePointer == 8){
- currentBytePointer = 0;
- outputStream.write(currentByte);
- currentByte = 0;
- }
- }
- }
-
- public void storeIntegerCompressed8(int data) throws IOException{
- if (data < 256){
- storeBits(0, 1);
- storeBits(data, 8);
- } else {
- storeBits(1, 1);
- storeBits(data, 32);
- }
- }
-
- public void finishByte() throws IOException {
- if (currentBytePointer != 0){
- outputStream.write(currentByte);
- currentBytePointer = 0;
- }
- }
-
-}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
public class Channel {
- byte [] rangeMap;
- byte [] map;
+ byte[] rangeMap;
+ byte[] map;
- byte [] decodedRangeMap;
- byte [] decodedMap;
+ byte[] decodedRangeMap;
+ byte[] decodedMap;
int bitCount;
-
- public Channel(int width, int 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[0] = (byte) 255;
decodedMap = new byte[width * height];
};
-
- public void reset(){
-
- for (int i=0; i < decodedMap.length; i++){
+ public void printStatistics() {
+ final float bitsPerPixel = (float) bitCount / (float) rangeMap.length;
+ System.out.println((bitCount / 8) + " bytes. " + bitsPerPixel
+ + " bits per pixel.");
+ }
+
+ public void reset() {
+
+ for (int i = 0; i < decodedMap.length; i++) {
decodedMap[i] = 0;
}
- for (int i=0; i < decodedRangeMap.length; i++){
+ for (int i = 0; i < decodedRangeMap.length; i++) {
decodedRangeMap[i] = 0;
}
- decodedRangeMap[0] = (byte)255;
-
- for (int i=0; i < map.length; i++){
+ decodedRangeMap[0] = (byte) 255;
+
+ for (int i = 0; i < map.length; i++) {
map[i] = 0;
}
- for (int i=0; i < rangeMap.length; i++){
+ for (int i = 0; i < rangeMap.length; i++) {
rangeMap[i] = 0;
}
-
+
bitCount = 0;
}
-
- public void printStatistics(){
- float bitsPerPixel = (float)bitCount / (float)rangeMap.length;
- System.out.println( (bitCount/8) + " bytes. " + bitsPerPixel + " bits per pixel.");
- }
-
+
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
/**
int u;
int v;
- public void YUV2RGB(){
+ public void RGB2YUV() {
- 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));
+ 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 (r < 0) r = 0;
- if (g < 0) g = 0;
- if (b < 0) b = 0;
+ if (y < 0)
+ y = 0;
+ if (u < 0)
+ u = 0;
+ if (v < 0)
+ v = 0;
- if (r > 255) r = 255;
- if (g > 255) g = 255;
- if (b > 255) b = 255;
+ if (y > 255)
+ y = 255;
+ if (u > 255)
+ u = 255;
+ if (v > 255)
+ v = 255;
}
- public void RGB2YUV(){
+ 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)));
- 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 (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
- if (y < 0) y = 0;
- if (u < 0) u = 0;
- if (v < 0) v = 0;
+ if (r > 255)
+ r = 255;
+ if (g > 255)
+ g = 255;
+ if (b > 255)
+ b = 255;
- if (y > 255) y = 255;
- if (u > 255) u = 255;
- if (v > 255) v = 255;
-
}
-
-};
+};
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
public class ColorStats {
int ySum;
int uSum;
int vSum;
-
+
int pixelCount;
-
-
- public ColorStats(){
+
+ public ColorStats() {
reset();
}
- public void reset(){
- ySum = 0;
- uSum = 0;
- vSum = 0;
- pixelCount = 0;
- }
-
- public int getAverageY(){
- return ySum / pixelCount;
+ 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 void reset() {
+ ySum = 0;
+ uSum = 0;
+ vSum = 0;
+ pixelCount = 0;
+ }
+}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.io.OutputStream;
+import eu.svjatoslav.commons.data.BitInputStream;
+import eu.svjatoslav.commons.data.BitOutputStream;
+
/**
* Main class representing compressed image.
*/
public BufferedImage bufferedImage;
ImageEncoder encoder;
-
- public Image(){};
+
+ public Image() {
+ };
/**
- * Initialize imagesqueeze image based on {@link BufferedImage}. {@link BufferedImage} must be of type BufferedImage.TYPE_3BYTE_BGR .
+ * Initialize imagesqueeze image based on {@link BufferedImage}.
+ * {@link BufferedImage} must be of type BufferedImage.TYPE_3BYTE_BGR .
*/
- public Image(BufferedImage image){
+ public Image(final BufferedImage image) {
- this.bufferedImage = image;
+ bufferedImage = image;
metaData = new ImageMetaData();
metaData.version = 1;
metaData.width = image.getWidth();
- metaData.height = image.getHeight();
+ metaData.height = image.getHeight();
}
/**
* Initialize empty imagesqueeze image.
*/
- public Image(int width, int height){
+ public Image(final int width, final int height) {
- this.bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
+ bufferedImage = new BufferedImage(width, height,
+ BufferedImage.TYPE_3BYTE_BGR);
metaData = new ImageMetaData();
metaData.version = 1;
metaData.width = width;
- metaData.height = height;
- }
-
- /**
- * Load ImgSqz image from {@link InputStream}.
- */
- public void loadImage(InputStream source) throws IOException{
- BitInputStream bitInputStream = new BitInputStream(source);
-
- metaData = new ImageMetaData();
- metaData.load(bitInputStream);
-
- bufferedImage = new BufferedImage(metaData.width, metaData.height, BufferedImage.TYPE_3BYTE_BGR);
-
- ImageDecoder imageDecoder = new ImageDecoder(this, bitInputStream);
-
- imageDecoder.decode();
+ metaData.height = height;
}
/**
* Load ImgSqz image from {@link File}.
*/
- public void loadImage(File source) throws IOException{
+ public void loadImage(final File source) throws IOException {
- byte [] fileContent = new byte[(int)source.length()];
+ final byte[] fileContent = new byte[(int) source.length()];
- FileInputStream fileInputStream = new FileInputStream(source);
+ final FileInputStream fileInputStream = new FileInputStream(source);
fileInputStream.read(fileContent);
fileInputStream.close();
- ByteArrayInputStream inputStream = new ByteArrayInputStream(fileContent);
+ final ByteArrayInputStream inputStream = new ByteArrayInputStream(
+ fileContent);
loadImage(inputStream);
}
/**
- * Save image into ImgSqz file format.
+ * Load ImgSqz image from {@link InputStream}.
*/
- public void saveImage(OutputStream outputStream) throws IOException{
+ public void loadImage(final InputStream source) throws IOException {
+ final BitInputStream bitInputStream = new BitInputStream(source);
- BitOutputStream bitOutputStream = new BitOutputStream(outputStream);
+ metaData = new ImageMetaData();
+ metaData.load(bitInputStream);
- metaData.save(bitOutputStream);
-
- if (encoder == null){
- encoder = new ImageEncoder(this);
- }
+ bufferedImage = new BufferedImage(metaData.width, metaData.height,
+ BufferedImage.TYPE_3BYTE_BGR);
- encoder.encode(bitOutputStream);
+ final ImageDecoder imageDecoder = new ImageDecoder(this, bitInputStream);
- bitOutputStream.finishByte();
+ imageDecoder.decode();
}
-
/**
* Save image into ImgSqz file format.
*/
- public void saveImage(File file) throws IOException{
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ public void saveImage(final File file) throws IOException {
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
saveImage(outputStream);
- byte [] buffer = outputStream.toByteArray();
- FileOutputStream fileOutputStream = new FileOutputStream(file);
+ final byte[] buffer = outputStream.toByteArray();
+ final FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(buffer);
fileOutputStream.close();
}
+ /**
+ * Save image into ImgSqz file format.
+ */
+ public void saveImage(final OutputStream outputStream) throws IOException {
+
+ final BitOutputStream bitOutputStream = new BitOutputStream(
+ outputStream);
+
+ metaData.save(bitOutputStream);
+
+ if (encoder == null) {
+ encoder = new ImageEncoder(this);
+ }
+
+ encoder.encode(bitOutputStream);
+
+ bitOutputStream.finishByte();
+ }
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
/**
import java.awt.image.WritableRaster;
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitInputStream;
+
public class ImageDecoder {
int width, height;
Image image;
- byte [] decodedYRangeMap;
- byte [] decodedYMap;
+ byte[] decodedYRangeMap;
+ byte[] decodedYMap;
- byte [] decodedURangeMap;
- byte [] decodedUMap;
+ byte[] decodedURangeMap;
+ byte[] decodedUMap;
- byte [] decodedVRangeMap;
- byte [] decodedVMap;
+ byte[] decodedVRangeMap;
+ byte[] decodedVMap;
Color tmpColor = new Color();
-
+
Approximator approximator;
BitInputStream bitInputStream;
-
+
ColorStats colorStats = new ColorStats();
OperatingContext context = new OperatingContext();
-
- public ImageDecoder (Image image, BitInputStream bitInputStream) {
+
+ public ImageDecoder(final Image image, final BitInputStream bitInputStream) {
approximator = new Approximator();
- this.image = image;
+ this.image = image;
this.bitInputStream = bitInputStream;
width = image.metaData.width;
height = image.metaData.height;
decodedYRangeMap = new byte[width * height];
- decodedYRangeMap[0] = (byte)(255);
+ decodedYRangeMap[0] = (byte) (255);
decodedYMap = new byte[width * height];
decodedURangeMap = new byte[width * height];
- decodedURangeMap[0] = (byte)(255);
+ decodedURangeMap[0] = (byte) (255);
decodedUMap = new byte[width * height];
decodedVRangeMap = new byte[width * height];
- decodedVRangeMap[0] = (byte)(255);
+ decodedVRangeMap[0] = (byte) (255);
decodedVMap = new byte[width * height];
}
-
public void decode() throws IOException {
approximator.load(bitInputStream);
approximator.computeLookupTables();
-
- WritableRaster raster = image.bufferedImage.getRaster();
- DataBufferByte dbi = (DataBufferByte)raster.getDataBuffer();
- byte [] pixels = dbi.getData();
-
- // load top-, left-most pixel.
- decodedYMap[0] = (byte)bitInputStream.readBits(8);
- decodedUMap[0] = (byte)bitInputStream.readBits(8);
- decodedVMap[0] = (byte)bitInputStream.readBits(8);
-
- Color color = new Color();
+
+ final WritableRaster raster = image.bufferedImage.getRaster();
+ final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+ final byte[] pixels = dbi.getData();
+
+ // load top-, left-most pixel.
+ decodedYMap[0] = (byte) bitInputStream.readBits(8);
+ decodedUMap[0] = (byte) bitInputStream.readBits(8);
+ decodedVMap[0] = (byte) bitInputStream.readBits(8);
+
+ final Color color = new Color();
color.y = ImageEncoder.byteToInt(decodedYMap[0]);
color.u = ImageEncoder.byteToInt(decodedUMap[0]);
color.v = ImageEncoder.byteToInt(decodedVMap[0]);
-
+
color.YUV2RGB();
-
- pixels[0] = (byte)color.r;
- pixels[0+1] = (byte)color.g;
- pixels[0+2] = (byte)color.b;
-
+ pixels[0] = (byte) color.r;
+ pixels[0 + 1] = (byte) color.g;
+ pixels[0 + 2] = (byte) color.b;
+
// detect initial step
int largestDimension;
int initialStep = 2;
} else {
largestDimension = height;
}
-
- while (initialStep < largestDimension){
+
+ while (initialStep < largestDimension) {
initialStep = initialStep * 2;
}
grid(initialStep, pixels);
}
-
- public void grid(int step, byte [] pixels) throws IOException {
+ public void grid(final int step, final byte[] pixels) throws IOException {
gridDiagonal(step / 2, step / 2, step, pixels);
gridSquare(step / 2, 0, step, pixels);
gridSquare(0, step / 2, step, pixels);
- if (step > 2) grid(step / 2, pixels);
- }
-
-
- public void gridSquare(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{
-
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
-
-
- int halfStep = step / 2;
-
- context.initialize(image, decodedYMap, decodedUMap, decodedVMap);
- context.measureNeighborEncode(x - halfStep, y);
- context.measureNeighborEncode(x + halfStep, y);
- context.measureNeighborEncode(x, y - halfStep);
- context.measureNeighborEncode(x, y + halfStep);
-
- loadPixel(step, offsetX, offsetY, x, y, pixels,
- context.colorStats.getAverageY(),
- context.colorStats.getAverageU(),
- context.colorStats.getAverageV());
-
- }
- }
+ if (step > 2)
+ grid(step / 2, pixels);
}
+ public void gridDiagonal(final int offsetX, final int offsetY,
+ final int step, final byte[] pixels) throws IOException {
- public void gridDiagonal(int offsetX, int offsetY, int step, byte [] pixels) throws IOException{
+ for (int y = offsetY; y < height; y = y + step) {
+ for (int x = offsetX; x < width; x = x + step) {
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
-
- int halfStep = step / 2;
+ final int halfStep = step / 2;
context.initialize(image, decodedYMap, decodedUMap, decodedVMap);
context.measureNeighborEncode(x - halfStep, y - halfStep);
context.measureNeighborEncode(x + halfStep, y - halfStep);
context.measureNeighborEncode(x - halfStep, y + halfStep);
context.measureNeighborEncode(x + halfStep, y + halfStep);
-
- loadPixel(step, offsetX, offsetY, x, y, pixels,
+
+ loadPixel(step, offsetX, offsetY, x, y, pixels,
context.colorStats.getAverageY(),
context.colorStats.getAverageU(),
context.colorStats.getAverageV());
- }
- }
+ }
+ }
}
+ public void gridSquare(final int offsetX, final int offsetY,
+ final int step, final byte[] pixels) throws IOException {
- public void loadPixel(int step, int offsetX, int offsetY, int x, int y, byte[] pixels,
- int averageDecodedY, int averageDecodedU, int averageDecodedV)
- throws IOException{
-
- int index = (y * width) + x;
-
- int halfStep = step / 2;
-
- int parentIndex;
- if (offsetX > 0){
- if (offsetY > 0){
- // diagonal approach
- parentIndex = ((y - halfStep) * width) + (x - halfStep);
- } else {
- // take left pixel
- parentIndex = (y * width) + (x - halfStep);
- }
- } else {
- // take upper pixel
- parentIndex = ((y - halfStep) * width) + x;
- }
-
+ for (int y = offsetY; y < height; y = y + step) {
+ for (int x = offsetX; x < width; x = x + step) {
+ final int halfStep = step / 2;
- int colorBufferIndex = index * 3;
+ 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);
- 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;
+ loadPixel(step, offsetX, offsetY, x, y, pixels,
+ context.colorStats.getAverageY(),
+ context.colorStats.getAverageU(),
+ context.colorStats.getAverageV());
+ }
+ }
}
-
- private int loadChannel(byte [] decodedRangeMap, byte [] decodedMap, Table table, int averageDecodedValue, int index,
- int parentIndex) throws IOException {
+ 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;
- int inheritedRange = ImageEncoder.byteToInt(decodedRangeMap[parentIndex]);
+ final int inheritedRange = ImageEncoder
+ .byteToInt(decodedRangeMap[parentIndex]);
int computedRange = inheritedRange;
- int bitCount = table.proposeBitcountForRange(inheritedRange);
+ final int bitCount = table.proposeBitcountForRange(inheritedRange);
int computedRangeBitCount = 0;
- if ( bitCount > 0){
+ if (bitCount > 0) {
- int rangeDecreases = bitInputStream.readBits(1);
- if (rangeDecreases != 0){
+ final int rangeDecreases = bitInputStream.readBits(1);
+ if (rangeDecreases != 0) {
computedRange = table.proposeDecreasedRange(inheritedRange);
- }
+ }
- decodedRangeMap[index] = (byte)computedRange;
- computedRangeBitCount = table.proposeBitcountForRange(computedRange);
+ decodedRangeMap[index] = (byte) computedRange;
+ computedRangeBitCount = table
+ .proposeBitcountForRange(computedRange);
- if (computedRangeBitCount > 0){
+ if (computedRangeBitCount > 0) {
- int encodedDifference = bitInputStream.readBits(computedRangeBitCount);
+ final int encodedDifference = bitInputStream
+ .readBits(computedRangeBitCount);
- int decodedDifference = ImageEncoder.decodeValueFromGivenBits(encodedDifference, computedRange, computedRangeBitCount);
+ final int decodedDifference = ImageEncoder
+ .decodeValueFromGivenBits(encodedDifference,
+ computedRange, computedRangeBitCount);
decodedValue = averageDecodedValue - decodedDifference;
- if (decodedValue > 255) decodedValue = 255;
- if (decodedValue < 0) decodedValue = 0;
+ if (decodedValue > 255)
+ decodedValue = 255;
+ if (decodedValue < 0)
+ decodedValue = 0;
}
} else {
- decodedRangeMap[index] = (byte)inheritedRange;
+ decodedRangeMap[index] = (byte) inheritedRange;
}
- decodedMap[index] = (byte)decodedValue;
+ 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;
+
+ }
+
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
/**
import java.awt.image.WritableRaster;
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitOutputStream;
public class ImageEncoder {
Channel yChannel;
Channel uChannel;
Channel vChannel;
-
+
Approximator approximator;
int bitsForY;
int bitsForU;
int bitsForV;
-
- //ColorStats colorStats = new ColorStats();
+
+ // ColorStats colorStats = new ColorStats();
OperatingContext context = new OperatingContext();
OperatingContext context2 = new OperatingContext();
BitOutputStream bitOutputStream;
- public ImageEncoder(Image image){
+ public ImageEncoder(final Image image) {
approximator = new Approximator();
- //bitOutputStream = outputStream;
+ // bitOutputStream = outputStream;
this.image = image;
}
-
- public void encode(BitOutputStream bitOutputStream) throws IOException {
+ public void encode(final BitOutputStream bitOutputStream)
+ throws IOException {
this.bitOutputStream = bitOutputStream;
-
+
approximator.initialize();
-
+
approximator.save(bitOutputStream);
-
+
width = image.metaData.width;
height = image.metaData.height;
- WritableRaster raster = image.bufferedImage.getRaster();
- DataBufferByte dbi = (DataBufferByte)raster.getDataBuffer();
- byte [] pixels = dbi.getData();
+ final WritableRaster raster = image.bufferedImage.getRaster();
+ final DataBufferByte dbi = (DataBufferByte) raster.getDataBuffer();
+ final byte[] pixels = dbi.getData();
- if (yChannel == null){
- yChannel = new Channel(width, height);
+ if (yChannel == null) {
+ yChannel = new Channel(width, height);
} else {
yChannel.reset();
}
-
- if (uChannel == null){
- uChannel = new Channel(width, height);
+
+ if (uChannel == null) {
+ uChannel = new Channel(width, height);
} else {
uChannel.reset();
}
-
- if (vChannel == null){
- vChannel = new Channel(width, height);
+
+ if (vChannel == null) {
+ vChannel = new Channel(width, height);
} else {
vChannel.reset();
}
-
+
// create YUV map out of RGB raster data
- Color color = new Color();
-
- for (int y=0; y < height; y++){
- for (int x=0; x < width; x++){
+ final Color color = new Color();
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
- int index = (y * width) + x;
- int colorBufferIndex = index * 3;
+ final int index = (y * width) + x;
+ final int colorBufferIndex = index * 3;
- int blue = pixels[colorBufferIndex];
- if (blue < 0) blue = blue + 256;
+ int blue = pixels[colorBufferIndex];
+ if (blue < 0)
+ blue = blue + 256;
- int green = pixels[colorBufferIndex+1];
- if (green < 0) green = green + 256;
+ int green = pixels[colorBufferIndex + 1];
+ if (green < 0)
+ green = green + 256;
- int red = pixels[colorBufferIndex+2];
- if (red < 0) red = red + 256;
+ int red = pixels[colorBufferIndex + 2];
+ if (red < 0)
+ red = red + 256;
color.r = red;
color.g = green;
color.RGB2YUV();
- yChannel.map[index] = (byte)color.y;
- uChannel.map[index] = (byte)color.u;
- vChannel.map[index] = (byte)color.v;
+ yChannel.map[index] = (byte) color.y;
+ uChannel.map[index] = (byte) color.u;
+ vChannel.map[index] = (byte) color.v;
}
}
yChannel.decodedMap[0] = yChannel.map[0];
uChannel.decodedMap[0] = uChannel.map[0];
vChannel.decodedMap[0] = vChannel.map[0];
-
- bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);
- bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);
- bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);
+
+ bitOutputStream.storeBits(byteToInt(yChannel.map[0]), 8);
+ bitOutputStream.storeBits(byteToInt(uChannel.map[0]), 8);
+ bitOutputStream.storeBits(byteToInt(vChannel.map[0]), 8);
// detect initial step
int largestDimension;
} else {
largestDimension = height;
}
-
- while (initialStep < largestDimension){
+
+ while (initialStep < largestDimension) {
initialStep = initialStep * 2;
}
-
+
rangeGrid(initialStep);
rangeRoundGrid(2);
saveGrid(initialStep);
}
- public void printStatistics(){
+ 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();
+ vChannel.printStatistics();
}
-
- public void rangeGrid(int step){
- //gridSquare(step / 2, step / 2, step, pixels);
+ 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);
+ 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) {
- public void rangeRoundGrid(int step){
-
- rangeRoundGridDiagonal(step / 2, step / 2, step);
- rangeRoundGridSquare(step / 2, 0, step);
- rangeRoundGridSquare(0, step / 2, step);
-
- if (step < 1024) rangeRoundGrid(step * 2);
- }
+ final int index = (y * width) + x;
+ final int halfStep = step / 2;
- public void saveGrid(int step) throws IOException {
+ context.initialize(image, yChannel.map, uChannel.map,
+ vChannel.map);
- saveGridDiagonal(step / 2, step / 2, step);
- saveGridSquare(step / 2, 0, step);
- saveGridSquare(0, step / 2, step);
+ context.measureNeighborEncode(x - halfStep, y - halfStep);
+ context.measureNeighborEncode(x + halfStep, y - halfStep);
+ context.measureNeighborEncode(x - halfStep, y + halfStep);
+ context.measureNeighborEncode(x + halfStep, y + halfStep);
- if (step > 2) saveGrid(step / 2);
+ 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) {
- public void rangeGridSquare(int offsetX, int offsetY, 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;
- int index = (y * width) + x;
- 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);
- 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 rangeGridDiagonal(int offsetX, int offsetY, int step){
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
+ public void rangeRoundGrid(final int step) {
- int index = (y * width) + x;
- int halfStep = step / 2;
-
- 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);
+ rangeRoundGridDiagonal(step / 2, step / 2, step);
+ rangeRoundGridSquare(step / 2, 0, step);
+ rangeRoundGridSquare(0, step / 2, step);
- yChannel.rangeMap[index] = (byte)context.getYRange(index);
- uChannel.rangeMap[index] = (byte)context.getURange(index);
- vChannel.rangeMap[index] = (byte)context.getVRange(index);
- }
- }
+ if (step < 1024)
+ rangeRoundGrid(step * 2);
}
- public void rangeRoundGridDiagonal(int offsetX, int offsetY, int step){
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
+ 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) {
- int index = (y * width) + x;
+ final int index = (y * width) + x;
- int yRange = byteToInt(yChannel.rangeMap[index]);
- int uRange = byteToInt(uChannel.rangeMap[index]);
- 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]);
- int halfStep = step / 2;
+ final int halfStep = step / 2;
- 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){
+ if (parentYRange < yRange) {
parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte)parentYRange;
+ yChannel.rangeMap[parentIndex] = (byte) parentYRange;
}
int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
- if (parentURange < uRange){
+ if (parentURange < uRange) {
parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte)parentURange;
+ uChannel.rangeMap[parentIndex] = (byte) parentURange;
}
int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
- if (parentVRange < vRange){
+ if (parentVRange < vRange) {
parentVRange = vRange;
- vChannel.rangeMap[parentIndex] = (byte)parentVRange;
+ vChannel.rangeMap[parentIndex] = (byte) parentVRange;
}
- }
- }
+ }
+ }
}
- public void rangeRoundGridSquare(int offsetX, int offsetY, int step){
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
+ 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) {
- int index = (y * width) + x;
-
- int yRange = byteToInt(yChannel.rangeMap[index]);
- int uRange = byteToInt(uChannel.rangeMap[index]);
- int vRange = byteToInt(vChannel.rangeMap[index]);
+ final int index = (y * width) + x;
- int halfStep = step / 2;
+ 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;
int parentIndex;
- if (offsetX > 0){
- parentIndex = (y * width) + (x - halfStep);
+ if (offsetX > 0) {
+ parentIndex = (y * width) + (x - halfStep);
} else {
- parentIndex = ((y - halfStep) * width) + x;
+ parentIndex = ((y - halfStep) * width) + x;
}
- int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
+ int parentYRange = byteToInt(yChannel.rangeMap[parentIndex]);
- if (parentYRange < yRange){
+ if (parentYRange < yRange) {
parentYRange = yRange;
- yChannel.rangeMap[parentIndex] = (byte)parentYRange;
+ yChannel.rangeMap[parentIndex] = (byte) parentYRange;
}
int parentURange = byteToInt(uChannel.rangeMap[parentIndex]);
- if (parentURange < uRange){
+ if (parentURange < uRange) {
parentURange = uRange;
- uChannel.rangeMap[parentIndex] = (byte)parentURange;
+ uChannel.rangeMap[parentIndex] = (byte) parentURange;
}
int parentVRange = byteToInt(vChannel.rangeMap[parentIndex]);
- if (parentVRange < vRange){
+ if (parentVRange < vRange) {
parentVRange = vRange;
- vChannel.rangeMap[parentIndex] = (byte)parentVRange;
+ vChannel.rangeMap[parentIndex] = (byte) parentVRange;
}
- }
- }
+ }
+ }
}
- public void saveGridSquare(int offsetX, int offsetY, int step) throws IOException{
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
-
- int halfStep = step / 2;
+ public void saveGrid(final int step) throws IOException {
- 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());
+ saveGridDiagonal(step / 2, step / 2, step);
+ saveGridSquare(step / 2, 0, step);
+ saveGridSquare(0, step / 2, step);
- }
- }
+ if (step > 2)
+ saveGrid(step / 2);
}
- public void saveGridDiagonal(int offsetX, int offsetY, int step) throws IOException {
- for (int y = offsetY; y < height; y = y + step){
- for (int x = offsetX; x < width; x = x + step){
-
- int halfStep = 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) {
- context2.initialize(image, yChannel.decodedMap, uChannel.decodedMap, vChannel.decodedMap);
+ 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);
-
-
+
savePixel(step, offsetX, offsetY, x, y,
context2.colorStats.getAverageY(),
context2.colorStats.getAverageU(),
- context2.colorStats.getAverageV());
-
- }
- }
+ context2.colorStats.getAverageV());
+
+ }
+ }
}
- public void savePixel(int step, int offsetX, int offsetY, int x, int y, int averageDecodedY, int averageDecodedU, int averageDecodedV) throws IOException {
+ 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 index = (y * width) + x;
+ final int halfStep = step / 2;
- int py = byteToInt(yChannel.map[index]);
- int pu = byteToInt(uChannel.map[index]);
- int pv = byteToInt(vChannel.map[index]);
-
- int yRange = byteToInt(yChannel.rangeMap[index]);
- int uRange = byteToInt(uChannel.rangeMap[index]);
- int vRange = byteToInt(vChannel.rangeMap[index]);
+ 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 halfStep = step / 2;
+ 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 {
+
+ 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 halfStep = step / 2;
int parentIndex;
- if (offsetX > 0){
- if (offsetY > 0){
+ if (offsetX > 0) {
+ if (offsetY > 0) {
// diagonal approach
- parentIndex = ((y - halfStep) * width) + (x - halfStep);
+ parentIndex = ((y - halfStep) * width) + (x - halfStep);
} else {
// take left pixel
- parentIndex = (y * width) + (x - halfStep);
- }
+ parentIndex = (y * width) + (x - halfStep);
+ }
} else {
// take upper pixel
- parentIndex = ((y - halfStep) * width) + x;
+ 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.vTable,
- vChannel,
- averageDecodedV,
- index,
- pv,
- vRange,
- 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);
}
+ public static int byteToInt(final byte input) {
+ int result = input;
+ if (result < 0)
+ result = result + 256;
+ return result;
+ }
- private void encodeChannel(Table table, Channel channel, int averageDecodedValue, int index,
- int value, int range, int parentIndex)
- throws IOException {
-
- byte[] decodedRangeMap = channel.decodedRangeMap;
- byte[] decodedMap = channel.decodedMap;
-
- int inheritedRange = byteToInt(decodedRangeMap[parentIndex]);
+ public static int decodeValueFromGivenBits(final int encodedBits,
+ final int range, final int bitCount) {
+ final int negativeBit = encodedBits & 1;
- int inheritedBitCount = table.proposeBitcountForRange(inheritedRange);
+ final int remainingBitCount = bitCount - 1;
- if (inheritedBitCount > 0){
- int computedRange;
- computedRange = table.proposeRangeForRange(range, inheritedRange);
- decodedRangeMap[index] = (byte)computedRange;
+ if (remainingBitCount == 0) {
+ // no more bits remaining to encode actual value
- channel.bitCount++;
- if (computedRange != inheritedRange){
- // brightness range shrinked
- bitOutputStream.storeBits(1, 1);
+ if (negativeBit == 0) {
+ return range;
} else {
- // brightness range stayed the same
- bitOutputStream.storeBits(0, 1);
+ return -range;
}
+ } else {
+ // still one or more bits left, encode value as precisely as
+ // possible
- // encode brightness into available amount of bits
- int computedBitCount = table.proposeBitcountForRange(computedRange);
-
- if (computedBitCount > 0){
+ final int encodedValue = (encodedBits >>> 1) + 1;
- int differenceToEncode = -(value - averageDecodedValue);
- int bitEncodedDifference = encodeValueIntoGivenBits(differenceToEncode, computedRange, computedBitCount);
+ final int realvalueForThisBitcount = 1 << remainingBitCount;
- channel.bitCount = channel.bitCount + computedBitCount;
- bitOutputStream.storeBits(bitEncodedDifference, computedBitCount);
+ // int valueMultiplier = range / realvalueForThisBitcount;
+ int decodedValue = (range * encodedValue)
+ / realvalueForThisBitcount;
- int decodedDifference = decodeValueFromGivenBits(bitEncodedDifference, computedRange, computedBitCount);
- int decodedValue = averageDecodedValue - decodedDifference;
- if (decodedValue > 255) decodedValue = 255;
- if (decodedValue < 0) decodedValue = 0;
+ if (decodedValue > range)
+ decodedValue = range;
- decodedMap[index] = (byte)decodedValue;
- } else {
- decodedMap[index] = (byte)averageDecodedValue;
- }
+ if (negativeBit == 0) {
+ return decodedValue;
+ } else {
+ return -decodedValue;
+ }
- } else {
- decodedRangeMap[index] = (byte)inheritedRange;
- decodedMap[index] = (byte)averageDecodedValue;
}
}
- public static int encodeValueIntoGivenBits(int value, int range, int bitCount){
+ public static int encodeValueIntoGivenBits(int value, final int range,
+ final int bitCount) {
int negativeBit = 0;
- if (value <0){
+ if (value < 0) {
negativeBit = 1;
value = -value;
}
- int remainingBitCount = bitCount - 1;
+ final int remainingBitCount = bitCount - 1;
- if (remainingBitCount == 0){
+ 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;
+ // still one or more bits left, encode value as precisely as
+ // possible
+ if (value > range)
+ value = range;
- int realvalueForThisBitcount = 1 << remainingBitCount;
+ final int realvalueForThisBitcount = 1 << remainingBitCount;
// int valueMultiplier = range / realvalueForThisBitcount;
- int encodedValue = value * realvalueForThisBitcount / range;
+ int encodedValue = (value * realvalueForThisBitcount) / range;
- if (encodedValue >= realvalueForThisBitcount) encodedValue = realvalueForThisBitcount - 1;
+ if (encodedValue >= realvalueForThisBitcount)
+ encodedValue = realvalueForThisBitcount - 1;
encodedValue = (encodedValue << 1) + negativeBit;
}
}
-
- public static int decodeValueFromGivenBits(int encodedBits, int range, int bitCount){
- int negativeBit = encodedBits & 1;
-
- 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
-
- int encodedValue = (encodedBits >>> 1) + 1;
-
- 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 byteToInt(byte input){
- int result = input;
- if (result < 0) result = result + 256;
- return result;
- }
-
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
/**
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitInputStream;
+import eu.svjatoslav.commons.data.BitOutputStream;
public class ImageMetaData {
int width;
int height;
+ public void load(final BitInputStream inputStream) throws IOException {
- public void load(BitInputStream inputStream) throws IOException{
-
- version = inputStream.readBits(16);
+ version = inputStream.readBits(16);
width = inputStream.readIntegerCompressed8();
height = inputStream.readIntegerCompressed8();
}
- public void save(BitOutputStream outputStream) throws IOException{
+ public void save(final BitOutputStream outputStream) throws IOException {
outputStream.storeBits(version, 16);
outputStream.storeIntegerCompressed8(width);
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
public class OperatingContext {
byte[] uMap;
byte[] vMap;
ColorStats colorStats = new ColorStats();
-
- public OperatingContext(){
+
+ public OperatingContext() {
}
- public void initialize(Image image, byte [] brightnessMap, byte [] colornessMap, byte [] colorMap){
- this.image = image;
- this.yMap = brightnessMap;
- this.uMap = colornessMap;
- this.vMap = colorMap;
-
- colorStats.reset();
+ public int getURange(final int index) {
+ final int colorness = ImageEncoder.byteToInt(uMap[index]);
+ return Math.abs(colorness - colorStats.getAverageU());
}
-
- public void measureNeighborEncode(int x, int y){
- if ((y >= 0) && (y < image.metaData.height) && (x >= 0) && (x < image.metaData.width)){
-
- 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++;
- }
+
+ public int getVRange(final int index) {
+ final int color = ImageEncoder.byteToInt(vMap[index]);
+ return Math.abs(color - colorStats.getAverageV());
}
- public int getYRange(int index){
- 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 int getURange(int index){
- int colorness = ImageEncoder.byteToInt(uMap[index]);
- return Math.abs(colorness - colorStats.getAverageU());
+ 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();
}
- public int getVRange(int index){
- int color = ImageEncoder.byteToInt(vMap[index]);
- return Math.abs(color - colorStats.getAverageV());
+ 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;
+
+ 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++;
+ }
}
-
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.codec;
import java.io.IOException;
+import eu.svjatoslav.commons.data.BitInputStream;
+import eu.svjatoslav.commons.data.BitOutputStream;
+
/**
* Quick lookup table.
*/
-public class Table implements Comparable<Table>{
+public class Table implements Comparable<Table> {
- int [] range = new int[100];
- int [] switchTreshold = new int[100];
- int [] bitcount = new int[100];
+ 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 [] 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;
- int usedEntries = 0;
+ 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;
+ }
- public void computeLookupTables(){
+ return 0;
+ }
+
+ public void computeLookupTables() {
int currentCheckPointer = 0;
- for (int i=0; i<256; i++){
+ for (int i = 0; i < 256; i++) {
- if (range[currentCheckPointer] == i){
+ if (range[currentCheckPointer] == i) {
currentCheckPointer++;
}
- if (currentCheckPointer > 0){
- bitCountForRange[i] = bitcount[currentCheckPointer-1];
+ if (currentCheckPointer > 0) {
+ bitCountForRange[i] = bitcount[currentCheckPointer - 1];
} else {
bitCountForRange[i] = 0;
}
}
- for (int i=0; i<256; i++){
+ for (int i = 0; i < 256; i++) {
int seek;
- seekLoop:{
- for (seek = 0; seek < usedEntries; seek ++){
+ seekLoop: {
+ for (seek = 0; seek < usedEntries; seek++) {
- if (switchTreshold[seek] >= i) break seekLoop;
+ if (switchTreshold[seek] >= i)
+ break seekLoop;
}
}
- proposedRangeForActualRange[i] = range[seek];
- if (seek == 0){
- proposedRangeForActualRangeLow[i] = 0;
+ proposedRangeForActualRange[i] = range[seek];
+ if (seek == 0) {
+ proposedRangeForActualRangeLow[i] = 0;
} else {
- proposedRangeForActualRangeLow[i] = switchTreshold[seek-1]+1;
+ proposedRangeForActualRangeLow[i] = switchTreshold[seek - 1] + 1;
}
- proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
+ proposedRangeForActualRangeHigh[i] = switchTreshold[seek];
}
-
currentCheckPointer = usedEntries - 2;
- for (int i=255; i >= 0; i--){
- if (range[currentCheckPointer] == i) currentCheckPointer--;
+ for (int i = 255; i >= 0; i--) {
+ if (range[currentCheckPointer] == i)
+ currentCheckPointer--;
- if (currentCheckPointer < 0){
+ if (currentCheckPointer < 0) {
proposedDecreasedRange[i] = 0;
} else {
- proposedDecreasedRange[i] = (byte)(range[currentCheckPointer]);
+ proposedDecreasedRange[i] = (byte) (range[currentCheckPointer]);
}
}
}
- /**
- * @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++;
- }
-
-
-
-
- public int proposeRangeForRange(int actualRange, int inheritedRange){
+ public void load(final BitInputStream inputStream) throws IOException {
+ reset();
- if (inheritedRange > 255) inheritedRange = 255;
- if (inheritedRange < 0) inheritedRange = 0;
+ final int availableEntries = inputStream.readIntegerCompressed8();
- if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange){
- return inheritedRange;
+ for (int i = 0; i < availableEntries; i++) {
+ addEntry(inputStream.readBits(8), inputStream.readBits(8),
+ inputStream.readBits(4));
}
-
- return proposeDecreasedRange(inheritedRange);
}
-
- public int proposeDecreasedRange(int range){
- if (range > 255) range = 255;
- if (range < 0) range = 0;
-
- return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
+ 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;
- public int proposeBitcountForRange(int range){
- if (range > 255) range = 255;
- if (range < 0) range = 0;
- int proposal = bitCountForRange[range];
- return proposal;
+ return ImageEncoder.byteToInt(proposedDecreasedRange[range]);
}
- /**
- * Compares two tables.
- * Ignores table initialization.
- */
- public int compareTo(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;
+ public int proposeRangeForRange(final int actualRange, int inheritedRange) {
- if (switchTreshold[i] < o.switchTreshold[i]) return -1;
- if (switchTreshold[i] > o.switchTreshold[i]) return 1;
+ if (inheritedRange > 255)
+ inheritedRange = 255;
+ if (inheritedRange < 0)
+ inheritedRange = 0;
- if (bitcount[i] < o.bitcount[i]) return -1;
- if (bitcount[i] > o.bitcount[i]) return 1;
+ if (proposedRangeForActualRangeLow[inheritedRange] <= actualRange) {
+ return inheritedRange;
}
- return 0;
+ return proposeDecreasedRange(inheritedRange);
}
- public void save(BitOutputStream outputStream) throws IOException {
- outputStream.storeIntegerCompressed8(usedEntries);
-
- for (int i=0; i < usedEntries; i++){
- outputStream.storeBits(this.range[i], 8);
- outputStream.storeBits(this.switchTreshold[i], 8);
- outputStream.storeBits(this.bitcount[i], 4);
- }
- }
-
- public void reset(){
- range = new int[100];
+ 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];
usedEntries = 0;
}
-
- public void load(BitInputStream inputStream) throws IOException {
- reset();
-
- int availableEntries = inputStream.readIntegerCompressed8();
-
- for (int i=0; i < availableEntries; i++){
- addEntry(inputStream.readBits(8), inputStream.readBits(8), inputStream.readBits(4));
- }
- }
+ public void save(final BitOutputStream outputStream) throws IOException {
+ outputStream.storeIntegerCompressed8(usedEntries);
+ for (int i = 0; i < usedEntries; i++) {
+ outputStream.storeBits(range[i], 8);
+ outputStream.storeBits(switchTreshold[i], 8);
+ outputStream.storeBits(bitcount[i], 4);
+ }
+ }
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.sampleApplication;
+
import java.awt.BorderLayout;
-import javax.swing.WindowConstants;
import javax.swing.SwingUtilities;
-
+import javax.swing.WindowConstants;
public class ImageFrame extends javax.swing.JFrame {
private ImagePanel imagePanel1;
- /**
- * Auto-generated main method to display this JFrame
- */
- public static void main(String[] args) {
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- ImageFrame inst = new ImageFrame("test");
- inst.setLocationRelativeTo(null);
- inst.setVisible(true);
- }
- });
- }
-
- public ImageFrame(String title) {
+ public ImageFrame(final String title) {
super();
setTitle(title);
initGUI();
}
-
+
+ public ImagePanel getImagePanel() {
+ return imagePanel1;
+ }
+
private void initGUI() {
try {
- BorderLayout thisLayout = new BorderLayout();
+ final BorderLayout thisLayout = new BorderLayout();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
getContentPane().setLayout(thisLayout);
{
getContentPane().add(getImagePanel(), BorderLayout.CENTER);
}
pack();
- } catch (Exception e) {
+ } catch (final Exception e) {
e.printStackTrace();
}
}
-
- public ImagePanel getImagePanel() {
- return imagePanel1;
+
+ /**
+ * 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);
+ }
+ });
}
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.sampleApplication;
import java.awt.BorderLayout;
-
import java.awt.Dimension;
import java.awt.image.BufferedImage;
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import javax.swing.JButton;
-
import javax.swing.ImageIcon;
-import javax.swing.JPanel;
-import javax.swing.WindowConstants;
import javax.swing.JFrame;
import javax.swing.JLabel;
+import javax.swing.WindowConstants;
import eu.svjatoslav.imagesqueeze.codec.Image;
-
public class ImagePanel extends javax.swing.JPanel {
private JLabel imageLabel;
public BufferedImage bufferedImage;
- /**
- * Auto-generated main method to display this
- * JPanel inside a new JFrame.
- */
- public static void main(String[] args) {
- JFrame frame = new JFrame();
- frame.getContentPane().add(new ImagePanel());
- frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
- frame.pack();
- frame.setVisible(true);
- }
-
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 {
- BorderLayout thisLayout = new BorderLayout();
- this.setLayout(thisLayout);
+ final BorderLayout thisLayout = new BorderLayout();
+ setLayout(thisLayout);
setPreferredSize(new Dimension(660, 500));
{
imageLabel = new JLabel();
this.add(getImageLabel(), BorderLayout.CENTER);
}
- } catch (Exception e) {
+ } catch (final Exception e) {
e.printStackTrace();
}
}
- public JLabel getImageLabel() {
- return imageLabel;
- }
+ public void loadImage(final File inputFile, final boolean isImgSqz)
+ throws IOException {
+ final FileInputStream fileInputStream = new FileInputStream(inputFile);
- public void loadImage(File inputFile, boolean isImgSqz) throws IOException{
- FileInputStream fileInputStream = new FileInputStream(inputFile);
-
- loadImage(fileInputStream, isImgSqz);
+ loadImage(fileInputStream, isImgSqz);
}
- public void loadImage(InputStream inputStream, boolean isImgSqz) throws IOException{
- if (isImgSqz){
+ public void loadImage(final InputStream inputStream, final boolean isImgSqz)
+ throws IOException {
+ if (isImgSqz) {
// load ImageSqueeze file
- Image image = new Image();
+ final Image image = new Image();
image.loadImage(inputStream);
bufferedImage = image.bufferedImage;
- ImageIcon icon = new ImageIcon(bufferedImage);
- //ImageIcon icon = new ImageIcon("sample data/original.png");
+ final ImageIcon icon = new ImageIcon(bufferedImage);
+ // ImageIcon icon = new ImageIcon("sample data/original.png");
- imageLabel.setIcon(icon);
+ imageLabel.setIcon(icon);
} else {
// load JPEG, PNG, GIF file
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
- readLoop:{
- for (;;){
- int b = inputStream.read();
- if (b == -1) break readLoop;
+ final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ readLoop: {
+ for (;;) {
+ final int b = inputStream.read();
+ if (b == -1)
+ break readLoop;
outputStream.write(b);
}
}
-
- ImageIcon icon = new ImageIcon(outputStream.toByteArray());
- bufferedImage = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_3BYTE_BGR);
+ 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);
- ImageIcon displayIcon = new ImageIcon(bufferedImage);
- imageLabel.setIcon(displayIcon);
+ final ImageIcon displayIcon = new ImageIcon(bufferedImage);
+ imageLabel.setIcon(displayIcon);
}
}
-
- public void createEmptyImage(Dimension dimension){
-
- bufferedImage = new BufferedImage(dimension.width, dimension.height, BufferedImage.TYPE_3BYTE_BGR);
-
- ImageIcon icon = new ImageIcon(bufferedImage);
-
- imageLabel.setIcon(icon);
- }
-
-
- public void saveImage(File outputFile){
- Image image = new Image(bufferedImage);
+ public void saveImage(final File outputFile) {
+ final Image image = new Image(bufferedImage);
try {
image.saveImage(outputFile);
- } catch (Exception e) {
+ } 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);
+ }
+
}
+/*
+ * Imagesqueeze - Image codec optimized for photos.
+ * Copyright (C) 2012, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
package eu.svjatoslav.imagesqueeze.sampleApplication;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
-
-
public class Main {
- public static void main(String[] args) {
+ public static void main(final String[] args) {
try {
- String image = "colorful photo";
- 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
- InputStream inputStream = Main.class.getResourceAsStream(sourceDirectory + image + ".png");
-
- ImageFrame frame = new ImageFrame("Original image");
- frame.getImagePanel().loadImage(inputStream, false);
- frame.setVisible(true);
+ final InputStream inputStream = Main.class
+ .getResourceAsStream(sourceDirectory + image + ".png");
+ 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"));
-
// create second frame for decoded image
- ImageFrame frame2 = new ImageFrame("Encoded -> Decoded");
+ final ImageFrame frame2 = new ImageFrame("Encoded -> Decoded");
// decode image
frame2.getImagePanel().loadImage(new File(image + ".ImgSqz"), true);
frame2.setVisible(true);
- } catch (IOException exception){
+ } catch (final IOException exception) {
System.out.println("Error while loading an image: " + exception);
}
-
}
}