package com.niklim.clicktrace.service;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.niklim.clicktrace.TimeMeter;
/**
* In parallel it compresses an image using given formats (jpg, png, etc.).
* Returns the image in the format resulting in the smallest file size.
*/
public class FileCompressor {
private static final Logger log = LoggerFactory.getLogger(FileCompressor.class);
ExecutorService executor;
String[] imageFormats;
public FileCompressor(String... imageFormats) {
executor = Executors.newFixedThreadPool(imageFormats.length);
this.imageFormats = imageFormats;
}
public CompressionResult getBestCompressed(BufferedImage image) {
TimeMeter tm = TimeMeter.start("FileCompressor.getBestCompressed", log);
List<Future<CompressionResult>> futures = compress(image);
CompressionResult minSize = null;
for (Future<CompressionResult> f : futures) {
try {
CompressionResult result = f.get();
if (minSize == null || result.streamSize < minSize.streamSize) {
minSize = result;
}
} catch (Exception e) {
log.error("", e);
}
}
tm.stop();
return minSize;
}
private List<Future<CompressionResult>> compress(BufferedImage image) {
List<Future<CompressionResult>> futures = new ArrayList<Future<CompressionResult>>(imageFormats.length);
for (String format : imageFormats) {
futures.add(executor.submit(new Compressing(image, format)));
}
return futures;
}
public static class CompressionResult {
public CompressionResult(ByteArrayOutputStream stream, String format) {
this.stream = stream;
this.streamSize = stream.size();
this.format = format;
}
public final ByteArrayOutputStream stream;
public final int streamSize;
public final String format;
}
private static class Compressing implements Callable<CompressionResult> {
private final BufferedImage image;
private final String format;
public Compressing(BufferedImage image, String format) {
this.image = image;
this.format = format;
}
@Override
public CompressionResult call() throws Exception {
try {
BufferedImage buffi = image;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(format);
ImageWriter writer = writers.next();
ImageWriteParam param = writer.getDefaultWriteParam();
writer.setOutput(ios);
writer.write(null, new IIOImage(buffi, null, null), param);
writer.dispose();
return new CompressionResult(baos, format);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}