package ij.plugin;
import ij.*;
import ij.process.*;
import ij.io.FileSaver;
import ij.io.SaveDialog;
import java.awt.image.*;
import java.awt.*;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.imageio.metadata.IIOMetadata;
public class JpegWriter implements PlugIn {
public static final int DEFAULT_QUALITY = 75;
private static boolean disableChromaSubsampling;
private static boolean chromaSubsamplingSet;
public void run(String arg) {
ImagePlus imp = WindowManager.getCurrentImage();
if (imp==null) return;
imp.startTiming();
saveAsJpeg(imp,arg,FileSaver.getJpegQuality());
IJ.showTime(imp, imp.getStartTime(), "JpegWriter: ");
}
public static String save(ImagePlus imp, String path, int quality) {
if (imp==null)
imp = IJ.getImage();
if (path==null || path.length()==0)
path = SaveDialog.getPath(imp, ".jpg");
if (path==null)
return null;
String error = (new JpegWriter()).saveAsJpeg(imp, path, quality);
return error;
}
String saveAsJpeg(ImagePlus imp, String path, int quality) {
int width = imp.getWidth();
int height = imp.getHeight();
int biType = BufferedImage.TYPE_INT_RGB;
boolean overlay = imp.getOverlay()!=null && !imp.getHideOverlay();
ImageProcessor ip = imp.getProcessor();
if (ip.isDefaultLut() && !imp.isComposite() && !overlay && ip.getMinThreshold()==ImageProcessor.NO_THRESHOLD)
biType = BufferedImage.TYPE_BYTE_GRAY;
BufferedImage bi = new BufferedImage(width, height, biType);
String error = null;
try {
Graphics g = bi.createGraphics();
Image img = imp.getImage();
if (overlay && !imp.tempOverlay())
img = imp.flatten().getImage();
g.drawImage(img, 0, 0, null);
g.dispose();
Iterator iter = ImageIO.getImageWritersByFormatName("jpeg");
ImageWriter writer = (ImageWriter)iter.next();
File f = new File(path);
String originalPath = null;
boolean replacing = f.exists();
if (replacing) {
originalPath = path;
path += ".temp";
f = new File(path);
}
ImageOutputStream ios = ImageIO.createImageOutputStream(f);
writer.setOutput(ios);
ImageWriteParam param = writer.getDefaultWriteParam();
param.setCompressionMode(param.MODE_EXPLICIT);
param.setCompressionQuality(quality/100f);
if (quality == 100)
param.setSourceSubsampling(1, 1, 0, 0);
IIOImage iioImage = null;
boolean disableSubsampling = quality>=90;
if (chromaSubsamplingSet)
disableSubsampling = disableChromaSubsampling;
if (!disableSubsampling) iioImage = new IIOImage(bi, null, null);
else {
IIOMetadata metadata = writer.getDefaultImageMetadata(new ImageTypeSpecifier(bi.getColorModel(), bi.getSampleModel()), param);
Node rootNode = metadata!=null ? metadata.getAsTree("javax_imageio_jpeg_image_1.0") : null;
boolean metadataUpdated = false;
if (rootNode!=null && rootNode.getLastChild() != null) {
Node markerNode = rootNode.getLastChild();
NodeList markers = markerNode.getChildNodes();
for (int i = 0; i < markers.getLength(); i++) {
Node node = markers.item(i);
if (node.getNodeName().equalsIgnoreCase("sof") && node.hasChildNodes() && node.getChildNodes().getLength() == 3) {
NamedNodeMap attrMap = node.getFirstChild().getAttributes();
int samplingmode = 17; attrMap.getNamedItem("HsamplingFactor").setNodeValue((samplingmode & 0xf) + "");
attrMap.getNamedItem("VsamplingFactor").setNodeValue(((samplingmode >> 4) & 0xf) + "");
metadataUpdated = true;
break;
}
}
}
if (metadataUpdated)
metadata.setFromTree("javax_imageio_jpeg_image_1.0", rootNode);
iioImage = new IIOImage(bi, null, metadata);
} writer.write(null, iioImage, param);
ios.close();
writer.dispose();
if (replacing) {
File f2 = new File(originalPath);
boolean ok = f2.delete();
if (ok) f.renameTo(f2);
}
} catch (Exception e) {
error = ""+e;
if (error.contains("Output has not been set!"))
error = "Incorrect file path: \""+path+"\"";
IJ.error("JPEG Writer", error);
}
return error;
}
public static void setQuality(int jpegQuality) {
FileSaver.setJpegQuality(jpegQuality);
}
public static int getQuality() {
return FileSaver.getJpegQuality();
}
public static void enhanceQuality(boolean enhance) {
disableChromaSubsampling = enhance;
chromaSubsamplingSet = true;
}
public static void disableChromaSubsampling(boolean disable) {
enhanceQuality(disable);
}
}