diff --git a/pom.xml b/pom.xml
index 47b3a21..7fef7fd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,15 +1,9 @@
+
4.0.0
org.atriasoft
png-encoder
0.1.0
-
- 3.13.0
- 21
- 21
- 3.1.1
-
-
gitea
@@ -36,7 +30,7 @@
org.junit.jupiter
junit-jupiter-api
- 5.11.0-M2
+ 5.12.2
test
@@ -48,23 +42,23 @@
- src
-
+ src/main
org.apache.maven.plugins
maven-compiler-plugin
- ${maven.compiler.version}
+ 3.14.0
- ${maven.compiler.source}
- ${maven.compiler.target}
-
+ 21
+ 21
+ UTF-8
org.apache.maven.plugins
maven-source-plugin
+ 3.3.1
attach-sources
@@ -78,7 +72,7 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.0.0-M5
+ 3.5.3
maven-assembly-plugin
@@ -93,30 +87,7 @@
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.2.0
-
- private
- true
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 3.2.0
-
- public
-
-
-
-
diff --git a/src/module-info.java b/src/module-info.java
deleted file mode 100644
index 23bb83a..0000000
--- a/src/module-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-module org.atriasoft.pngencoder {
- exports org.atriasoft.pngencoder;
-
- requires transitive org.atriasoft.egami;
- requires transitive org.atriasoft.etk;
-}
\ No newline at end of file
diff --git a/src/org/atriasoft/pngencoder/PngEncoder.java b/src/org/atriasoft/pngencoder/PngEncoder.java
deleted file mode 100644
index bffed89..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoder.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.zip.Deflater;
-
-import org.atriasoft.egami.Image;
-
-/**
- * Main class, containing the interface for PngEncoder.
- * PngEncoder is a really fast encoder for PNG images in Java.
- */
-public class PngEncoder {
- /**
- * Compression level 9 is the default.
- * It produces images with a size comparable to ImageIO.
- */
- public static int DEFAULT_COMPRESSION_LEVEL = Deflater.BEST_COMPRESSION;
-
- private final Image bufferedImage;
- private final int compressionLevel;
- private final boolean multiThreadedCompressionEnabled;
- private final PngEncoderPhysicalPixelDimensions physicalPixelDimensions;
- private final PngEncoderSrgbRenderingIntent srgbRenderingIntent;
-
- /**
- * Constructs an empty PngEncoder. Usually combined with methods named with*.
- */
- public PngEncoder() {
- this(null, PngEncoder.DEFAULT_COMPRESSION_LEVEL, true, null, null);
- }
-
- private PngEncoder(final Image bufferedImage, final int compressionLevel, final boolean multiThreadedCompressionEnabled, final PngEncoderSrgbRenderingIntent srgbRenderingIntent,
- final PngEncoderPhysicalPixelDimensions physicalPixelDimensions) {
- this.bufferedImage = bufferedImage;
- this.compressionLevel = PngEncoderVerificationUtil.verifyCompressionLevel(compressionLevel);
- this.multiThreadedCompressionEnabled = multiThreadedCompressionEnabled;
- this.srgbRenderingIntent = srgbRenderingIntent;
- this.physicalPixelDimensions = physicalPixelDimensions;
- }
-
- public Image getBufferedImage() {
- return this.bufferedImage;
- }
-
- public int getCompressionLevel() {
- return this.compressionLevel;
- }
-
- public PngEncoderSrgbRenderingIntent getSrgbRenderingIntent() {
- return this.srgbRenderingIntent;
- }
-
- public boolean isMultiThreadedCompressionEnabled() {
- return this.multiThreadedCompressionEnabled;
- }
-
- /**
- * Encodes the image and returns data as {@code byte[]}.
- * @throws NullPointerException if the image has not been set.
- * @return encoded data
- */
- public byte[] toBytes() {
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(64 * 1024);
- toStream(outputStream);
- return outputStream.toByteArray();
- }
-
- /**
- * Encodes the image and saves data into {@code file}.
- * @param file destination file where the encoded data will be written
- * @throws NullPointerException if the image has not been set.
- * @throws UncheckedIOException instead of IOException
- * @return number of bytes written
- */
- public int toFile(final File file) {
- return toFile(file.toPath());
- }
-
- /**
- * Encodes the image and saves data into {@code filePath}.
- * @param filePath destination file where the encoded data will be written
- * @throws NullPointerException if the image has not been set.
- * @throws UncheckedIOException instead of IOException
- * @return number of bytes written
- */
- public int toFile(final Path filePath) {
- try (OutputStream outputStream = Files.newOutputStream(filePath)) {
- return toStream(outputStream);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
- * Encodes the image and saves data into {@code fileName}.
- * @param fileName destination file where the encoded data will be written
- * @throws NullPointerException if the image has not been set.
- * @throws UncheckedIOException instead of IOException
- * @return number of bytes written
- */
- public int toFile(final String fileName) {
- return toFile(Paths.get(fileName));
- }
-
- /**
- * Encodes the image to outputStream.
- * @param outputStream destination of the encoded data
- * @throws NullPointerException if the image has not been set.
- * @return number of bytes written
- */
- public int toStream(final OutputStream outputStream) {
- try {
- return PngEncoderLogic.encode(this.bufferedImage, outputStream, this.compressionLevel, this.multiThreadedCompressionEnabled, this.srgbRenderingIntent, this.physicalPixelDimensions);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /**
- * Returns a new PngEncoder which has the same configuration as this one except {@code bufferedImage}.
- * The new PngEncoder will use the provided {@code bufferedImage}.
- *
- * @param bufferedImage input image
- * @return a new PngEncoder
- */
- public PngEncoder withBufferedImage(final Image bufferedImage) {
- return new PngEncoder(bufferedImage, this.compressionLevel, this.multiThreadedCompressionEnabled, this.srgbRenderingIntent, this.physicalPixelDimensions);
- }
-
- /**
- * Returns a new PngEncoder which has the same configuration as this one except {@code compressionLevel}.
- * The new PngEncoder will use the provided {@code compressionLevel}.
- *
- * @param compressionLevel input image (must be between -1 and 9 inclusive)
- * @return a new PngEncoder
- */
- public PngEncoder withCompressionLevel(final int compressionLevel) {
- return new PngEncoder(this.bufferedImage, compressionLevel, this.multiThreadedCompressionEnabled, this.srgbRenderingIntent, this.physicalPixelDimensions);
- }
-
- /**
- * Returns a new PngEncoder which has the same configuration as this one except {@code multiThreadedCompressionEnabled}.
- * The new PngEncoder will use the provided {@code multiThreadedCompressionEnabled}.
- *
- * @param multiThreadedCompressionEnabled when {@code true}, multithreaded compression will be used
- * @return a new PngEncoder
- */
- public PngEncoder withMultiThreadedCompressionEnabled(final boolean multiThreadedCompressionEnabled) {
- return new PngEncoder(this.bufferedImage, this.compressionLevel, multiThreadedCompressionEnabled, this.srgbRenderingIntent, this.physicalPixelDimensions);
- }
-
- public PngEncoder withPhysicalPixelDimensions(final PngEncoderPhysicalPixelDimensions physicalPixelDimensions) {
- return new PngEncoder(this.bufferedImage, this.compressionLevel, this.multiThreadedCompressionEnabled, this.srgbRenderingIntent, physicalPixelDimensions);
- }
-
- /**
- * Returns a new PngEncoder which has the same configuration as this one except {@code srgbRenderingIntent}.
- * The new PngEncoder will add an sRGB chunk to the encoded PNG and use the provided {@code srgbRenderingIntent}.
- *
- * @param srgbRenderingIntent the rendering intent that should be used when displaying the image
- * @return a new PngEncoder
- */
- public PngEncoder withSrgbRenderingIntent(final PngEncoderSrgbRenderingIntent srgbRenderingIntent) {
- return new PngEncoder(this.bufferedImage, this.compressionLevel, this.multiThreadedCompressionEnabled, srgbRenderingIntent, this.physicalPixelDimensions);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderCountingOutputStream.java b/src/org/atriasoft/pngencoder/PngEncoderCountingOutputStream.java
deleted file mode 100644
index 9d81519..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderCountingOutputStream.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Objects;
-
-class PngEncoderCountingOutputStream extends FilterOutputStream {
- private int count;
-
- PngEncoderCountingOutputStream(final OutputStream out) {
- super(Objects.requireNonNull(out, "out"));
- }
-
- public int getCount() {
- return this.count;
- }
-
- @Override
- public void write(final byte[] b, final int off, final int len) throws IOException {
- this.out.write(b, off, len);
- this.count += len;
- }
-
- @Override
- public void write(final int b) throws IOException {
- this.out.write(b);
- this.count++;
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterBuffer.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterBuffer.java
deleted file mode 100644
index ed1b0ac..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterBuffer.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Objects;
-import java.util.zip.Adler32;
-
-class PngEncoderDeflaterBuffer {
- final byte[] bytes;
- int length;
- final PngEncoderDeflaterBufferPool pool;
-
- PngEncoderDeflaterBuffer(final PngEncoderDeflaterBufferPool pool, final int maxLength) {
- this.pool = Objects.requireNonNull(pool, "pool");
- this.bytes = new byte[maxLength];
- this.length = 0;
- }
-
- long calculateAdler32() {
- Adler32 adler32 = new Adler32();
- adler32.update(this.bytes, 0, this.length);
- return adler32.getValue();
- }
-
- void giveBack() {
- this.pool.giveBack(this);
- }
-
- void write(final OutputStream outputStream) throws IOException {
- outputStream.write(this.bytes, 0, this.length);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterBufferPool.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterBufferPool.java
deleted file mode 100644
index 7a2dfdc..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterBufferPool.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.LinkedList;
-import java.util.Queue;
-
-class PngEncoderDeflaterBufferPool {
- private final int bufferMaxLength;
- protected final Queue buffers;
-
- PngEncoderDeflaterBufferPool(final int bufferMaxLength) {
- this.bufferMaxLength = bufferMaxLength;
- this.buffers = new LinkedList<>();
- }
-
- PngEncoderDeflaterBuffer borrow() {
- PngEncoderDeflaterBuffer buffer = this.buffers.poll();
- if (buffer == null) {
- buffer = new PngEncoderDeflaterBuffer(this, this.bufferMaxLength);
- }
- return buffer;
- }
-
- public int getBufferMaxLength() {
- return this.bufferMaxLength;
- }
-
- void giveBack(final PngEncoderDeflaterBuffer buffer) {
- buffer.length = 0;
- this.buffers.offer(buffer);
- }
-
- int size() {
- return this.buffers.size();
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorService.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorService.java
deleted file mode 100644
index 4c4d838..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorService.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-class PngEncoderDeflaterExecutorService {
- private static class Holder {
- private static final ExecutorService INSTANCE = Executors.newFixedThreadPool(PngEncoderDeflaterExecutorService.NUM_THREADS_IS_AVAILABLE_PROCESSORS,
- PngEncoderDeflaterExecutorServiceThreadFactory.getInstance());
- }
-
- public static int NUM_THREADS_IS_AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
-
- static ExecutorService getInstance() {
- return Holder.INSTANCE;
- }
-
- private PngEncoderDeflaterExecutorService() {}
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorServiceThreadFactory.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorServiceThreadFactory.java
deleted file mode 100644
index e99a178..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterExecutorServiceThreadFactory.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.atomic.AtomicLong;
-
-class PngEncoderDeflaterExecutorServiceThreadFactory implements ThreadFactory {
- private static class Holder {
- private static final PngEncoderDeflaterExecutorServiceThreadFactory INSTANCE = new PngEncoderDeflaterExecutorServiceThreadFactory();
- }
-
- static PngEncoderDeflaterExecutorServiceThreadFactory getInstance() {
- return Holder.INSTANCE;
- }
-
- private final AtomicLong counter;
- private final ThreadFactory defaultThreadFactory;
-
- PngEncoderDeflaterExecutorServiceThreadFactory() {
- this.defaultThreadFactory = Executors.defaultThreadFactory();
- this.counter = new AtomicLong(0);
- }
-
- @Override
- public Thread newThread(final Runnable runnable) {
- Thread thread = this.defaultThreadFactory.newThread(runnable);
- thread.setName("PngEncoder Deflater (" + this.counter.getAndIncrement() + ")");
- thread.setDaemon(true);
- return thread;
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterOutputStream.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterOutputStream.java
deleted file mode 100644
index 2a01137..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterOutputStream.java
+++ /dev/null
@@ -1,206 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-// https://tools.ietf.org/html/rfc1950
-// https://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
-// https://www.euccas.me/zlib/
-// https://stackoverflow.com/questions/13132136/java-multithreaded-compression-with-deflater
-class PngEncoderDeflaterOutputStream extends FilterOutputStream {
- // The maximum amount of queued tasks.
- // Multiplied because some segments compress faster than others.
- // A value of 3 seems to keep all threads busy.
- static final int COUNT_MAX_QUEUED_TASKS = PngEncoderDeflaterExecutorService.NUM_THREADS_IS_AVAILABLE_PROCESSORS * 3;
-
- // Enforces writing to underlying stream in main thread.
- // Multiplied so that not all work is finished before flush to underlying stream.
- static final int COUNT_MAX_TOTAL_SEGMENTS = PngEncoderDeflaterOutputStream.COUNT_MAX_QUEUED_TASKS * 3;
-
- // The maximum dictionary size according to the deflate specification.
- // A segment max length lower than this would not allow for future use of dictionary.
- // Used for unit test sanity checking.
- static final int SEGMENT_MAX_LENGTH_DICTIONARY = 32 * 1024;
-
- // Our minimum segment length.
- // Corresponds to about 2% size overhead.
- // A lower value would better parallelize images but increase the size overhead.
- static final int SEGMENT_MAX_LENGTH_ORIGINAL_MIN = 128 * 1024;
-
- static byte getFlg(final int compressionLevel) {
- if (compressionLevel == -1 || compressionLevel == 6) {
- return (byte) 0x9C;
- }
-
- if (compressionLevel >= 0 && compressionLevel <= 1) {
- return (byte) 0x01;
- }
-
- if (compressionLevel >= 2 && compressionLevel <= 5) {
- return (byte) 0x5E;
- }
-
- if (compressionLevel >= 7 && compressionLevel <= 9) {
- return (byte) 0xDA;
- }
-
- throw new IllegalArgumentException("Invalid compressionLevel: " + compressionLevel);
- }
-
- public static int getSegmentMaxLengthDeflated(final int segmentMaxLengthOriginal) {
- return segmentMaxLengthOriginal + (segmentMaxLengthOriginal >> 3);
- }
-
- public static int getSegmentMaxLengthOriginal(final int totalOriginalBytesLength) {
- return Math.max(totalOriginalBytesLength / PngEncoderDeflaterOutputStream.COUNT_MAX_TOTAL_SEGMENTS, PngEncoderDeflaterOutputStream.SEGMENT_MAX_LENGTH_ORIGINAL_MIN);
- }
-
- static void writeDeflateHeader(final OutputStream outputStream, final int compressionLevel) throws IOException {
- // Write "CMF"
- // " ... In practice, this means the first byte is almost always 78 (hex) ..."
- outputStream.write(0x78);
-
- // Write "FLG"
- byte flg = PngEncoderDeflaterOutputStream.getFlg(compressionLevel);
- outputStream.write(flg);
- }
-
- private long adler32;
- private boolean closed;
- private final int compressionLevel;
- private boolean finished;
- private PngEncoderDeflaterBuffer originalSegment;
- private final PngEncoderDeflaterBufferPool pool;
- private final ConcurrentLinkedQueue> resultQueue;
-
- private final int segmentMaxLengthOriginal;
-
- private final byte[] singleByte;
-
- PngEncoderDeflaterOutputStream(final OutputStream out, final int compressionLevel, final int segmentMaxLengthOriginal) throws IOException {
- this(out, compressionLevel, segmentMaxLengthOriginal, new PngEncoderDeflaterBufferPool(PngEncoderDeflaterOutputStream.getSegmentMaxLengthDeflated(segmentMaxLengthOriginal)));
- }
-
- PngEncoderDeflaterOutputStream(final OutputStream out, final int compressionLevel, final int segmentMaxLengthOriginal, final PngEncoderDeflaterBufferPool pool) throws IOException {
- super(Objects.requireNonNull(out, "out"));
- this.pool = Objects.requireNonNull(pool, "pool");
- this.singleByte = new byte[1];
- this.compressionLevel = compressionLevel;
- this.segmentMaxLengthOriginal = segmentMaxLengthOriginal;
- this.resultQueue = new ConcurrentLinkedQueue<>();
- this.originalSegment = pool.borrow();
- this.adler32 = 1;
- this.finished = false;
- this.closed = false;
- if (pool.getBufferMaxLength() != PngEncoderDeflaterOutputStream.getSegmentMaxLengthDeflated(segmentMaxLengthOriginal)) {
- throw new IllegalArgumentException("Mismatch between segmentMaxLengthOriginal and pool.");
- }
- PngEncoderDeflaterOutputStream.writeDeflateHeader(out, compressionLevel);
- }
-
- @Override
- public void close() throws IOException {
- if (this.closed) {
- return;
- }
- this.closed = true;
- finish();
- super.close();
- }
-
- public void finish() throws IOException {
- if (this.finished) {
- return;
- }
- this.finished = true;
- try {
- submitTask(true);
- joinUntilMaximumQueueSize(0);
- this.out.write(ByteBuffer.allocate(4).putInt((int) this.adler32).array());
- this.out.flush();
- } finally {
- this.originalSegment.giveBack();
- }
- }
-
- void joinOne() throws IOException {
- CompletableFuture resultFuture = this.resultQueue.poll();
- if (resultFuture != null) {
- final PngEncoderDeflaterSegmentResult result;
- try {
- result = resultFuture.join();
- } catch (RuntimeException e) {
- throw new IOException("An async segment task failed.", e);
- }
- try {
- this.adler32 = result.getUpdatedAdler32(this.adler32);
- result.getDeflatedSegment().write(this.out);
- } finally {
- result.getOriginalSegment().giveBack();
- result.getDeflatedSegment().giveBack();
- }
- }
- }
-
- void joinUntilMaximumQueueSize(final int maximumResultQueueSize) throws IOException {
- while (this.resultQueue.size() > maximumResultQueueSize) {
- joinOne();
- }
- }
-
- void submitTask(final boolean lastSegment) {
- final PngEncoderDeflaterBuffer deflatedSegment = this.pool.borrow();
- final PngEncoderDeflaterSegmentTask task = new PngEncoderDeflaterSegmentTask(this.originalSegment, deflatedSegment, this.compressionLevel, lastSegment);
- submitTask(task);
- this.originalSegment = this.pool.borrow();
- }
-
- void submitTask(final PngEncoderDeflaterSegmentTask task) {
- CompletableFuture future = CompletableFuture.supplyAsync(task, PngEncoderDeflaterExecutorService.getInstance());
- this.resultQueue.offer(future);
- }
-
- @Override
- public void write(final byte[] b) throws IOException {
- write(b, 0, b.length);
- }
-
- @Override
- public void write(final byte[] b, int off, int len) throws IOException {
- if (this.finished) {
- throw new IOException("write beyond end of stream");
- }
- if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
- throw new IndexOutOfBoundsException();
- }
- if (len == 0) {
- return;
- }
-
- while (len > 0) {
- int freeBufCount = this.segmentMaxLengthOriginal - this.originalSegment.length;
- if (freeBufCount == 0) {
- // Submit task if the buffer is full and there still is more to write.
- joinUntilMaximumQueueSize(PngEncoderDeflaterOutputStream.COUNT_MAX_QUEUED_TASKS - 1);
- submitTask(false);
- } else {
- int toCopyCount = Math.min(len, freeBufCount);
- System.arraycopy(b, off, this.originalSegment.bytes, this.originalSegment.length, toCopyCount);
- this.originalSegment.length += toCopyCount;
- off += toCopyCount;
- len -= toCopyCount;
- }
- }
- }
-
- @Override
- public void write(final int b) throws IOException {
- this.singleByte[0] = (byte) (b & 0xff);
- write(this.singleByte, 0, 1);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentResult.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentResult.java
deleted file mode 100644
index cc40a94..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentResult.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.Objects;
-
-class PngEncoderDeflaterSegmentResult {
- // https://github.com/madler/zlib/blob/master/adler32.c#L143
- static long combine(final long adler1, final long adler2, final long len2) {
- long BASEL = 65521;
- long sum1;
- long sum2;
- long rem;
-
- rem = len2 % BASEL;
- sum1 = adler1 & 0xffffL;
- sum2 = rem * sum1;
- sum2 %= BASEL;
- sum1 += (adler2 & 0xffffL) + BASEL - 1;
- sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem;
- if (sum1 >= BASEL) {
- sum1 -= BASEL;
- }
- if (sum1 >= BASEL) {
- sum1 -= BASEL;
- }
- if (sum2 >= (BASEL << 1)) {
- sum2 -= (BASEL << 1);
- }
- if (sum2 >= BASEL) {
- sum2 -= BASEL;
- }
- return sum1 | (sum2 << 16);
- }
-
- private final PngEncoderDeflaterBuffer deflatedSegment;
- private final PngEncoderDeflaterBuffer originalSegment;
- private final long originalSegmentAdler32;
-
- private final int originalSegmentLength;
-
- PngEncoderDeflaterSegmentResult(final PngEncoderDeflaterBuffer originalSegment, final PngEncoderDeflaterBuffer deflatedSegment, final long originalSegmentAdler32,
- final int originalSegmentLength) {
- this.originalSegment = Objects.requireNonNull(originalSegment, "originalSegment");
- this.deflatedSegment = Objects.requireNonNull(deflatedSegment, "deflatedSegment");
- this.originalSegmentAdler32 = originalSegmentAdler32;
- this.originalSegmentLength = originalSegmentLength;
- }
-
- public PngEncoderDeflaterBuffer getDeflatedSegment() {
- return this.deflatedSegment;
- }
-
- public PngEncoderDeflaterBuffer getOriginalSegment() {
- return this.originalSegment;
- }
-
- long getUpdatedAdler32(final long originalAdler32) {
- return PngEncoderDeflaterSegmentResult.combine(originalAdler32, this.originalSegmentAdler32, this.originalSegmentLength);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentTask.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentTask.java
deleted file mode 100644
index 04cf6db..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterSegmentTask.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.Objects;
-import java.util.function.Supplier;
-import java.util.zip.Deflater;
-
-class PngEncoderDeflaterSegmentTask implements Supplier {
- static void deflate(final PngEncoderDeflaterBuffer originalSegment, final PngEncoderDeflaterBuffer deflatedSegment, final int compressionLevel, final boolean lastSegment) {
- final Deflater deflater = PngEncoderDeflaterThreadLocalDeflater.getInstance(compressionLevel);
- deflater.setInput(originalSegment.bytes, 0, originalSegment.length);
-
- if (lastSegment) {
- deflater.finish();
- }
-
- deflatedSegment.length = deflater.deflate(deflatedSegment.bytes, 0, deflatedSegment.bytes.length, lastSegment ? Deflater.NO_FLUSH : Deflater.SYNC_FLUSH);
- }
-
- private final int compressionLevel;
- private final PngEncoderDeflaterBuffer deflatedSegment;
- private final boolean lastSegment;
-
- private final PngEncoderDeflaterBuffer originalSegment;
-
- public PngEncoderDeflaterSegmentTask(final PngEncoderDeflaterBuffer originalSegment, final PngEncoderDeflaterBuffer deflatedSegment, final int compressionLevel, final boolean lastSegment) {
- this.originalSegment = Objects.requireNonNull(originalSegment, "originalSegment");
- this.deflatedSegment = Objects.requireNonNull(deflatedSegment, "deflatedSegment");
- this.compressionLevel = compressionLevel;
- this.lastSegment = lastSegment;
- }
-
- @Override
- public PngEncoderDeflaterSegmentResult get() {
- final long originalSegmentAdler32 = this.originalSegment.calculateAdler32();
- final int originalSegmentLength = this.originalSegment.length;
-
- PngEncoderDeflaterSegmentTask.deflate(this.originalSegment, this.deflatedSegment, this.compressionLevel, this.lastSegment);
-
- return new PngEncoderDeflaterSegmentResult(this.originalSegment, this.deflatedSegment, originalSegmentAdler32, originalSegmentLength);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderDeflaterThreadLocalDeflater.java b/src/org/atriasoft/pngencoder/PngEncoderDeflaterThreadLocalDeflater.java
deleted file mode 100644
index f795bdc..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderDeflaterThreadLocalDeflater.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.util.zip.Deflater;
-
-/**
- * We save time by allocating and reusing some thread local state.
- *
- * Creating a new Deflater instance takes a surprising amount of time.
- * Resetting an existing Deflater instance is almost free though.
- */
-class PngEncoderDeflaterThreadLocalDeflater {
- private static final ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(PngEncoderDeflaterThreadLocalDeflater::new);
-
- static Deflater getInstance(final int compressionLevel) {
- return PngEncoderDeflaterThreadLocalDeflater.THREAD_LOCAL.get().getDeflater(compressionLevel);
- }
-
- private final Deflater[] deflaters;
-
- private PngEncoderDeflaterThreadLocalDeflater() {
- this.deflaters = new Deflater[11];
- for (int compressionLevel = -1; compressionLevel <= 9; compressionLevel++) {
- boolean nowrap = true;
- this.deflaters[compressionLevel + 1] = new Deflater(compressionLevel, nowrap);
- }
- }
-
- private Deflater getDeflater(final int compressionLevel) {
- Deflater deflater = this.deflaters[compressionLevel + 1];
- deflater.reset();
- return deflater;
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderIdatChunksOutputStream.java b/src/org/atriasoft/pngencoder/PngEncoderIdatChunksOutputStream.java
deleted file mode 100644
index 817bb4b..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderIdatChunksOutputStream.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.zip.CRC32;
-
-class PngEncoderIdatChunksOutputStream extends FilterOutputStream {
- // An IDAT chunk adds 12 bytes of overhead to the data within.
- // 12 / (32 * 1024) = 0.00037 meaning the size overhead is just 0.037% which should be negligible.
- static final int DEFAULT_BUFFER_LENGTH = 32 * 1024;
-
- static final byte[] IDAT_BYTES = "IDAT".getBytes(StandardCharsets.US_ASCII);
-
- private final byte[] buf;
- private int count;
- private final CRC32 crc;
-
- PngEncoderIdatChunksOutputStream(final OutputStream out) {
- this(out, PngEncoderIdatChunksOutputStream.DEFAULT_BUFFER_LENGTH);
- }
-
- PngEncoderIdatChunksOutputStream(final OutputStream out, final int bufferLength) {
- super(out);
- this.crc = new CRC32();
- this.buf = new byte[bufferLength];
- this.count = 0;
- }
-
- @Override
- public void flush() throws IOException {
- flushBuffer();
- super.flush();
- }
-
- private void flushBuffer() throws IOException {
- if (this.count > 0) {
- writeIdatChunk(this.buf, 0, this.count);
- this.count = 0;
- }
- }
-
- @Override
- public void write(final byte[] b) throws IOException {
- write(b, 0, b.length);
- }
-
- @Override
- public void write(final byte[] b, final int off, final int len) throws IOException {
- if (len >= this.buf.length) {
- flushBuffer();
- writeIdatChunk(b, off, len);
- return;
- }
- if (len > this.buf.length - this.count) {
- flushBuffer();
- }
- System.arraycopy(b, off, this.buf, this.count, len);
- this.count += len;
- }
-
- @Override
- public void write(final int b) throws IOException {
- if (this.count >= this.buf.length) {
- flushBuffer();
- }
- this.buf[this.count++] = (byte) b;
- }
-
- private void writeIdatChunk(final byte[] b, final int off, final int len) throws IOException {
- writeInt(len);
- this.out.write(PngEncoderIdatChunksOutputStream.IDAT_BYTES);
- this.out.write(b, off, len);
- this.crc.reset();
- this.crc.update(PngEncoderIdatChunksOutputStream.IDAT_BYTES);
- this.crc.update(b, off, len);
- writeInt((int) this.crc.getValue());
- }
-
- private void writeInt(final int i) throws IOException {
- this.out.write((byte) (i >> 24) & 0xFF);
- this.out.write((byte) (i >> 16) & 0xFF);
- this.out.write((byte) (i >> 8) & 0xFF);
- this.out.write((byte) i & 0xFF);
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderLogic.java b/src/org/atriasoft/pngencoder/PngEncoderLogic.java
deleted file mode 100644
index 3de5403..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderLogic.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package org.atriasoft.pngencoder;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Objects;
-import java.util.zip.CRC32;
-import java.util.zip.Deflater;
-import java.util.zip.DeflaterOutputStream;
-
-import org.atriasoft.egami.ToolImage;
-import org.atriasoft.egami.Image;
-
-class PngEncoderLogic {
- public static final byte[] CHRM_SRGB_VALUE = ByteBuffer.allocate(8 * 4).putInt(31270).putInt(32900).putInt(64000).putInt(33000).putInt(30000).putInt(60000).putInt(15000).putInt(6000).array();
-
- // In hex: 89 50 4E 47 0D 0A 1A 0A
- // This is the "file beginning" aka "header" aka "signature" aka "magicnumber".
- // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
- // http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2
- // All PNGs start this way and it does not include any pixel format info.
- static final byte[] FILE_BEGINNING = { -119, 80, 78, 71, 13, 10, 26, 10 };
-
- // In hex: 00 00 00 00 49 45 4E 44 AE 42 60 82
- // This is the "file ending"
- static final byte[] FILE_ENDING = { 0, 0, 0, 0, 73, 69, 78, 68, -82, 66, 96, -126 };
- // Default values for the gAMA and cHRM chunks when an sRGB chunk is used,
- // as specified at http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.sRGB
- // "An application that writes the sRGB chunk should also write a gAMA chunk (and perhaps a cHRM chunk)
- // for compatibility with applications that do not use the sRGB chunk.
- // In this situation, only the following values may be used:
- // ..."
- public static final byte[] GAMA_SRGB_VALUE = ByteBuffer.allocate(4).putInt(45455).array();
- static final byte IHDR_BIT_DEPTH = 8;
- static final byte IHDR_COLOR_TYPE_RGB = 2;
- static final byte IHDR_COLOR_TYPE_RGBA = 6;
- static final byte IHDR_COMPRESSION_METHOD = 0;
-
- static final byte IHDR_FILTER_METHOD = 0;
- static final byte IHDR_INTERLACE_METHOD = 0;
-
- static byte[] asChunk(final String type, final byte[] data) {
- PngEncoderVerificationUtil.verifyChunkType(type);
- ByteBuffer byteBuffer = ByteBuffer.allocate(data.length + 12);
- byteBuffer.putInt(data.length);
- ByteBuffer byteBufferForCrc = byteBuffer.slice().asReadOnlyBuffer();
- byteBufferForCrc.limit(4 + data.length);
- byteBuffer.put(type.getBytes(StandardCharsets.US_ASCII));
- byteBuffer.put(data);
- byteBuffer.putInt(PngEncoderLogic.getCrc32(byteBufferForCrc));
- return byteBuffer.array();
- }
-
- static int encode(final Image bufferedImage, final OutputStream outputStream, final int compressionLevel, final boolean multiThreadedCompressionEnabled,
- final PngEncoderSrgbRenderingIntent srgbRenderingIntent, final PngEncoderPhysicalPixelDimensions physicalPixelDimensions) throws IOException {
- Objects.requireNonNull(bufferedImage, "bufferedImage");
- Objects.requireNonNull(outputStream, "outputStream");
-
- final boolean alpha = bufferedImage.hasAlpha();
- final int width = bufferedImage.getWidth();
- final int height = bufferedImage.getHeight();
- final PngEncoderCountingOutputStream countingOutputStream = new PngEncoderCountingOutputStream(outputStream);
-
- countingOutputStream.write(PngEncoderLogic.FILE_BEGINNING);
-
- final byte[] ihdr = PngEncoderLogic.getIhdrHeader(width, height, alpha);
- final byte[] ihdrChunk = PngEncoderLogic.asChunk("IHDR", ihdr);
- countingOutputStream.write(ihdrChunk);
-
- if (srgbRenderingIntent != null) {
- outputStream.write(PngEncoderLogic.asChunk("sRGB", new byte[] { srgbRenderingIntent.getValue() }));
- outputStream.write(PngEncoderLogic.asChunk("gAMA", PngEncoderLogic.GAMA_SRGB_VALUE));
- outputStream.write(PngEncoderLogic.asChunk("cHRM", PngEncoderLogic.CHRM_SRGB_VALUE));
- }
-
- if (physicalPixelDimensions != null) {
- outputStream.write(PngEncoderLogic.asChunk("pHYs", PngEncoderLogic.getPhysicalPixelDimensions(physicalPixelDimensions)));
- }
-
- PngEncoderIdatChunksOutputStream idatChunksOutputStream = new PngEncoderIdatChunksOutputStream(countingOutputStream);
- final byte[] scanlineBytes;
-
- if (bufferedImage.hasAlpha()) {
- scanlineBytes = ToolImage.pngConvertInByteBufferRGBA(bufferedImage);
- } else {
- scanlineBytes = ToolImage.pngConvertInByteBufferRGB(bufferedImage);
- }
-
- final int segmentMaxLengthOriginal = PngEncoderDeflaterOutputStream.getSegmentMaxLengthOriginal(scanlineBytes.length);
-
- if (scanlineBytes.length <= segmentMaxLengthOriginal || !multiThreadedCompressionEnabled) {
- Deflater deflater = new Deflater(compressionLevel);
- DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(idatChunksOutputStream, deflater);
- deflaterOutputStream.write(scanlineBytes);
- deflaterOutputStream.finish();
- deflaterOutputStream.flush();
- //deflaterOutputStream.close();
- } else {
- PngEncoderDeflaterOutputStream deflaterOutputStream = new PngEncoderDeflaterOutputStream(idatChunksOutputStream, compressionLevel, segmentMaxLengthOriginal);
- deflaterOutputStream.write(scanlineBytes);
- deflaterOutputStream.finish();
- //deflaterOutputStream.close();
- }
-
- countingOutputStream.write(PngEncoderLogic.FILE_ENDING);
-
- countingOutputStream.flush();
-
- return countingOutputStream.getCount();
- }
-
- static int getCrc32(final ByteBuffer byteBuffer) {
- CRC32 crc = new CRC32();
- crc.update(byteBuffer);
- return (int) crc.getValue();
- }
-
- static byte[] getIhdrHeader(final int width, final int height, final boolean alpha) {
- ByteBuffer buffer = ByteBuffer.allocate(13);
- buffer.putInt(width);
- buffer.putInt(height);
- buffer.put(PngEncoderLogic.IHDR_BIT_DEPTH);
- buffer.put(alpha ? PngEncoderLogic.IHDR_COLOR_TYPE_RGBA : PngEncoderLogic.IHDR_COLOR_TYPE_RGB);
- buffer.put(PngEncoderLogic.IHDR_COMPRESSION_METHOD);
- buffer.put(PngEncoderLogic.IHDR_FILTER_METHOD);
- buffer.put(PngEncoderLogic.IHDR_INTERLACE_METHOD);
- return buffer.array();
- }
-
- static byte[] getPhysicalPixelDimensions(final PngEncoderPhysicalPixelDimensions physicalPixelDimensions) {
- ByteBuffer buffer = ByteBuffer.allocate(9);
- buffer.putInt(physicalPixelDimensions.getPixelsPerUnitX());
- buffer.putInt(physicalPixelDimensions.getPixelsPerUnitY());
- buffer.put(physicalPixelDimensions.getUnit().getValue());
- return buffer.array();
- }
-
- private PngEncoderLogic() {}
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderPhysicalPixelDimensions.java b/src/org/atriasoft/pngencoder/PngEncoderPhysicalPixelDimensions.java
deleted file mode 100644
index 97f4157..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderPhysicalPixelDimensions.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package org.atriasoft.pngencoder;
-
-/**
- * Represents PNG physical pixel dimensions
- *
- * Use one of the static methods to create physical pixel dimensions based
- * on pixels per meter, dots per inch or a unit-less aspect ratio.
- *
- * @see https://www.w3.org/TR/PNG/#11pHYs
- */
-public class PngEncoderPhysicalPixelDimensions {
-
- public enum Unit {
- METER((byte) 1), UNKNOWN((byte) 0);
-
- private final byte value;
-
- Unit(final byte value) {
- this.value = value;
- }
-
- public byte getValue() {
- return this.value;
- }
- }
-
- private static final float INCHES_PER_METER = 100 / 2.54f;
-
- /**
- * Creates a PngEncoderPhysicalPixelDimensions that only specifies the aspect ratio,
- * but not the size, of the pixels
- *
- * @param pixelsPerUnitX the number of pixels per unit in the horizontal dimension
- * @param pixelsPerUnitY the number of pixels per unit in the vertical dimension
- */
- public static PngEncoderPhysicalPixelDimensions aspectRatio(final int pixelsPerUnitX, final int pixelsPerUnitY) {
- return new PngEncoderPhysicalPixelDimensions(pixelsPerUnitX, pixelsPerUnitY, Unit.UNKNOWN);
- }
-
- /**
- * Creates a PngEncoderPhysicalPixelDimensions with square pixels
- * with a size specified in dots per inch
- *
- * Note that dots per inch (DPI) cannot be exactly represented by the PNG format's
- * integer value for pixels per meter. There will be a slight rounding error.
- *
- * @param dotsPerInch the DPI value for both dimensions
- */
- public static PngEncoderPhysicalPixelDimensions dotsPerInch(final int dotsPerInch) {
- return PngEncoderPhysicalPixelDimensions.dotsPerInch(dotsPerInch, dotsPerInch);
- }
-
- /**
- * Creates a PngEncoderPhysicalPixelDimensions with possibly non-square pixels
- * with a size specified in dots per inch
- *
- * Note that dots per inch (DPI) cannot be exactly represented by the PNG format's
- * integer value for pixels per meter. There will be a slight rounding error.
- *
- * @param dotsPerInchX the DPI value for the horizontal dimension
- * @param dotsPerInchY the DPI value for the vertical dimension
- */
- public static PngEncoderPhysicalPixelDimensions dotsPerInch(final int dotsPerInchX, final int dotsPerInchY) {
- int pixelsPerMeterX = Math.round(dotsPerInchX * PngEncoderPhysicalPixelDimensions.INCHES_PER_METER);
- int pixelsPerMeterY = Math.round(dotsPerInchY * PngEncoderPhysicalPixelDimensions.INCHES_PER_METER);
-
- return new PngEncoderPhysicalPixelDimensions(pixelsPerMeterX, pixelsPerMeterY, Unit.METER);
- }
-
- /**
- * Creates a PngEncoderPhysicalPixelDimensions with square pixels
- * with a size specified in pixels per meter
- *
- * @param pixelsPerMeter the pixels per meter value for both dimensions
- */
- public static PngEncoderPhysicalPixelDimensions pixelsPerMeter(final int pixelsPerMeter) {
- return PngEncoderPhysicalPixelDimensions.pixelsPerMeter(pixelsPerMeter, pixelsPerMeter);
- }
-
- /**
- * Creates a PngEncoderPhysicalPixelDimensions with possibly non-square pixels
- * with a size specified in pixels per meter
- *
- * @param pixelsPerMeterX the pixels per meter value for the horizontal dimension
- * @param pixelsPerMeterY the pixels per meter value for the vertical dimension
- */
- public static PngEncoderPhysicalPixelDimensions pixelsPerMeter(final int pixelsPerMeterX, final int pixelsPerMeterY) {
- return new PngEncoderPhysicalPixelDimensions(pixelsPerMeterX, pixelsPerMeterY, Unit.METER);
- }
-
- private final int pixelsPerUnitX;
-
- private final int pixelsPerUnitY;
-
- private final Unit unit;
-
- private PngEncoderPhysicalPixelDimensions(final int pixelsPerUnitX, final int pixelsPerUnitY, final Unit unit) {
- this.pixelsPerUnitX = pixelsPerUnitX;
- this.pixelsPerUnitY = pixelsPerUnitY;
- this.unit = unit;
- }
-
- /**
- * @return the number of pixels per unit in the horizontal dimension
- */
- public int getPixelsPerUnitX() {
- return this.pixelsPerUnitX;
- }
-
- /**
- * @return the number of pixels per unit in the vertical dimension
- */
- public int getPixelsPerUnitY() {
- return this.pixelsPerUnitY;
- }
-
- /**
- * @return the unit of the pixel size (either {@link Unit#METER} or {@link Unit#UNKNOWN})
- */
- public Unit getUnit() {
- return this.unit;
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderSrgbRenderingIntent.java b/src/org/atriasoft/pngencoder/PngEncoderSrgbRenderingIntent.java
deleted file mode 100644
index 1340972..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderSrgbRenderingIntent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.atriasoft.pngencoder;
-
-public enum PngEncoderSrgbRenderingIntent {
- ABSOLUTE_COLORIMETRIC((byte) 3), PERCEPTUAL((byte) 0), RELATIVE_COLORIMETRIC((byte) 1), SATURATION((byte) 2);
-
- private final byte value;
-
- PngEncoderSrgbRenderingIntent(final byte value) {
- this.value = value;
- }
-
- public byte getValue() {
- return this.value;
- }
-}
diff --git a/src/org/atriasoft/pngencoder/PngEncoderVerificationUtil.java b/src/org/atriasoft/pngencoder/PngEncoderVerificationUtil.java
deleted file mode 100644
index 4981ec6..0000000
--- a/src/org/atriasoft/pngencoder/PngEncoderVerificationUtil.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.atriasoft.pngencoder;
-
-class PngEncoderVerificationUtil {
- static String verifyChunkType(final String chunkType) {
- if (chunkType.length() != 4) {
- String message = String.format("The chunkType must be four letters, but was \"%s\". See http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.1", chunkType);
- throw new IllegalArgumentException(message);
- }
- return chunkType;
- }
-
- static int verifyCompressionLevel(final int compressionLevel) {
- if ((compressionLevel < -1) || (compressionLevel > 9)) {
- String message = String.format("The compressionLevel must be between -1 and 9 inclusive, but was %d.", compressionLevel);
- throw new IllegalArgumentException(message);
- }
- return compressionLevel;
- }
-
- private PngEncoderVerificationUtil() {}
-}