Java Wrapper for the fast, native FPNG Encoder and the AVX optimized native FPNGe Encoder. Contains 64 bit binaries for Windows, Linux and MacOS, built and tested on GitHub Runners. Unfortunately, MacOS ARM64 on Apple Silicon is not supported yet.
An additional SSE translation from Java's ABGR into the expected RGBA arrays is provided. (The AVX version has been tested to be slower).
License: GNU Affero General Public License, Version 3 or later.
Maven and Gradle artifacts are available, please see below.
import com.manticore.tools.FPNGEncoder; // when using the SSE version
import com.manticore.tools.FPNGE; // when using the AVX version
FPNGEncoder.encode(bufferedImage, 3, 0); // encoding with 3 channels and fastest compression
FPNGE.encode(bufferedImage, 3, 5); // encoding with 3 channels and best compressionThere are 7 projects included:
encoderis an abstract base class for loading the native libraries, byte arrays and testsfpngis the C++ source from FPNG with an additional C wrapperfpng-javaprovides the JavaFPNGEencoder, depending onfpngandJNAfpngeis the AVX optimized C++ source from FPNGe with an additional C wrapperfpnge-javaprovides the JavaFPNGEncoder, depending onfpngeandJNAbenchmarkare optional JMH based performance testsmaven-testas a most simple Java project stub for testing the Maven dependencies and the Native Libs on various OS after publishing.
The following Gradle task will compile FPNG with -O3 -march=x86-64 -mtune=generic and wrap it into a JAR via JNA.
git clone --depth 1 https://github.com/manticore-projects/fpng-java.git
cd fpng-java
gradle clean assembleThe artifacts will be written to: .fpng-java/build/libs/fpng-java-1.2.0-SNAPSHOT.jar and .fpnge-java/build/libs/fpnge-java-1.2.0-SNAPSHOT.jar
There is a JMH based benchmark suite comparing other Java PNG Encoders, using one small and one very large PNG:
gradle clean assemble jmh# JMH version: 1.37, Blackhole mode: compiler
# VM version: JDK 21.0.1, OpenJDK 64-Bit Server VM, 21.0.1+12-jvmci-23.1-b19 -XX:+UseSerialGC -Xms512M -Xmx2G -XX:+UseStringDeduplication
Benchmark (channels) (imageName) Mode Cnt Score Error Units
FPNGEBenchmark.encode 3 example.png avgt 10 1.993 ± 0.008 ms/op
FPNGEBenchmark.encode 3 looklet-look-scale6.png avgt 10 84.272 ± 0.860 ms/op
FPNGEBenchmark.encode 4 example.png avgt 10 2.976 ± 0.014 ms/op
FPNGEBenchmark.encode 4 looklet-look-scale6.png avgt 10 126.364 ± 11.213 ms/op
FPNGEncoderBenchmark.encode 3 example.png avgt 10 5.057 ± 0.046 ms/op
FPNGEncoderBenchmark.encode 3 looklet-look-scale6.png avgt 10 196.432 ± 0.836 ms/op
FPNGEncoderBenchmark.encode 4 example.png avgt 10 6.434 ± 0.054 ms/op
FPNGEncoderBenchmark.encode 4 looklet-look-scale6.png avgt 10 268.631 ± 12.623 ms/op
ImageIOEncoderBenchmark.encode 3 example.png avgt 10 54.189 ± 0.422 ms/op
ImageIOEncoderBenchmark.encode 3 looklet-look-scale6.png avgt 10 1099.045 ± 13.667 ms/op
ImageIOEncoderBenchmark.encode 4 example.png avgt 10 64.538 ± 0.605 ms/op
ImageIOEncoderBenchmark.encode 4 looklet-look-scale6.png avgt 10 1270.494 ± 6.797 ms/op
ObjectPlanetPNGEncoderBenchmark.encode 3 example.png avgt 10 43.910 ± 0.248 ms/op
ObjectPlanetPNGEncoderBenchmark.encode 3 looklet-look-scale6.png avgt 10 1491.651 ± 8.445 ms/op
ObjectPlanetPNGEncoderBenchmark.encode 4 example.png avgt 10 53.449 ± 0.279 ms/op
ObjectPlanetPNGEncoderBenchmark.encode 4 looklet-look-scale6.png avgt 10 1776.148 ± 7.182 ms/op
PNGEncoderBenchmark.encode 3 example.png avgt 10 39.813 ± 0.517 ms/op
PNGEncoderBenchmark.encode 3 looklet-look-scale6.png avgt 10 854.307 ± 2.627 ms/op
PNGEncoderBenchmark.encode 4 example.png avgt 10 44.704 ± 0.252 ms/op
PNGEncoderBenchmark.encode 4 looklet-look-scale6.png avgt 10 1120.410 ± 6.746 ms/op
PNGEncoderBenchmark.encodeFastest 3 example.png avgt 10 23.990 ± 0.173 ms/op
PNGEncoderBenchmark.encodeFastest 3 looklet-look-scale6.png avgt 10 311.706 ± 1.456 ms/op
PNGEncoderBenchmark.encodeFastest 4 example.png avgt 10 28.218 ± 0.202 ms/op
PNGEncoderBenchmark.encodeFastest 4 looklet-look-scale6.png avgt 10 428.018 ± 5.281 ms/op
Remark: Score in milli-seconds per encoding, smaller is better.
The compression rates were set to MEDIUM for achieving comparable file-sizes. The Java Encoders are able to achieve better compression rates at an even higher performance penalty.
FPNG SSE an FPNGe AVX achieve very competitive file-sizes for smaller images but fall-off considerably for the very large image (1.5x the size of ImageIO). Please see details in the Benchmark Spreadsheet.
<repositories>
<!-- Only needed when using the FPNG SSE2 Encoder -->
<repository>
<id>sonatype-snapshots</id>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
<dependencies>
<!-- Only needed when using the FPNG SSE2 Encoder -->
<dependency>
<groupId>com.manticore-projects.tools</groupId>
<artifactId>fpng-java</artifactId>
<version>1.2.0</version>
</dependency>
<!-- Only needed when using the FPNGe AVX2 Encoder -->
<dependency>
<groupId>com.manticore-projects.tools</groupId>
<artifactId>fpnge-java</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>repositories {
mavenCentral()
maven {
url = uri('https://s01.oss.sonatype.org/content/repositories/releases/')
}
// Only needed when loading Snapshots
maven {
url = uri('https://s01.oss.sonatype.org/content/repositories/snapshots/')
}
}
dependencies {
// Only needed when using the FPNG SSE2 Encoder
implementation 'com.manticore-projects.tools:fpng-java:+'
// Only needed when using the FPNGe AVX2 Encoder
implementation 'com.manticore-projects.tools:fpnge-java:+'
}- Add more test images for the "screen capturing" use case, which may yield different outcomes. Right now only photo-realistic images are tested.
- Drop slow JNA and replace with a JNI implementation.
- Try profiling with PGO.