1.在项目中为了处理用户上传的图片,会对图片进行重写处理,故使用了类似下面的代码:
File src = new File("e:/img/google.jpg"); BufferedImage bi = (BufferedImage)ImageIO.read(src); BufferedImage bi_scale = new BufferedImage(bi.getWidth(), bi.getHeight(), BufferedImage.TYPE_INT_RGB ); Graphics2D g = bi_scale.createGraphics(); g.drawImage( bi, 0, 0, bi.getWidth(), bi.getHeight(), null ); g.dispose(); FileOutputStream fos = new FileOutputStream("e:/img/google2.jpg"); ImageIO.write(bi_scale, "jpg", fos); fos.close();
原图如下:
但发现对于CMYK模式的图片ImageIO.read方法在读取时,会丢失掉ICC信息,从面导致重写后的图片上面蒙受上一层红色如下:
显然对图片质量的影响非常大,在用户体验非常高的互联网上是没法让用户接受的,于是又改用另外一种获取Image对象的方法如下:
Image image = Toolkit.getDefaultToolkit().getImage("e:\img\google.jpg");
但该方法在Windows平台下使用正常,而在Linux平台上总是报异常如下:
java.awt.HeadlessException: No X11 DISPLAY variable was set, but this program performed an operation which requires it. at sun.awt.HeadlessToolkit.getScreenSize(HeadlessToolkit.java:281) at ScreenCapture.main(ScreenCapture.java:23)
然后按照网上的方法,运行的时候加了-Djava.awt.headless=true即 java -Djava.awt.headless=true ClassName 但接着报如下异常:
java.awt.HeadlessException java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:159) java.awt.Window.(Window.java:406)
原因是:Toolkit.getDefaultToolkit()方法中有一句
str = System.getProperty("awt.toolkit", "sun.awt.X11.XToolkit");
其实这句话的意思是该程序运行需要有图片环境的支持,后来经过测试,在Linux开了图形窗口后运行也是正常的,但对于生产环境的服务器要开个图形界面是不太现实的,最终放弃了这种方案。经过探索在Linux下只能选用ImageMagick结合JMagick实现,此时能独立于X-11系统,请看下篇,ImageMagick,Jmagick-linux_64安装说明
注意:
1.使用Jimi1.0 版本,保存PICT文件格式时输出的图像宽、高和内存中的图像源一致,可是显示出来却是纯白的。这是 Jimi1.0 版本对写Pict格式的一处 BUG,下载了新的补订类PICTWriter.java 重新编译成PICTWriter.class加入 JimiProClasses.zip 中覆盖原有的 PICTWriter.class,遂写 PICT 格式成功。可以参考官方说明:http://amug.org/~glguerin/other/index.html#PICTWriter 编译时注意要兼容你的JDK版本。
2.关于ImageIO.read()方法读取图片时不能正确处理ICC信息的问题,一般这些图片,是由扫描仪扫描,或者数码相机拍摄,并且处理时,图片的颜色空间(colorspace)为CMYK模式,可以参考官方BUG说明:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6245283
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6404011
另外处理相关ICC信息的代码如下:
ICC_Profile ICC_pf= ICC_Profile.getInstance( "e:/img/jaicmm/CMYK.pf"); ICC_ColorSpace ICC_ClSpace= new ICC_ColorSpace(ICC_pf); 其中*.pf色彩文件从 http://java.sun.com/products/java-media/jai/downloads/download-1_0_2.html可取得。
在经过一系列探索,《ImageIO.read()方法读取图片后重写,图片蒙上一层红色的解决办法(上)》篇已经通过Jimi初步解决,但在Linux平台下必须开图形界面才行,但现实情况,服务器一般运行在init 3级别下,init 5不太现实,另外Jimi对象图片的处理也较差,部分图片重写后质量降低,模糊等,由此也证明了Java语言在处理图片上的劣势,这是非常让人纠心的事,最终只能借助于无敌的ImageMagick结合对应的JNI接口JMagick来完成,上篇《ImageMagick,Jmagick-linux_64安装说明》已经介绍了该软件在Linux下的安装过程,下面我们看看使用JMagick如何实现图片高质量重写。
import java.io.IOException; import magick.ImageInfo; import magick.MagickImage; /** * Created on 2011-11-1 *@author:[
[email protected]] */ public class ImageTypeTest { static { if (System.getProperty("jmagick.systemclassloader") == null) { System.setProperty("jmagick.systemclassloader", "no"); System.err.println(System.getProperty("java.library.path")); } try { MagickImage.class.getClass(); } catch (Error e) { System.err.println("Make sure JMagick libraries are available in java.library.path. Current value: "); System.err.println("java.library.path=" + System.getProperty("java.library.path")); throw e; } } /** * Created on 2011-11-1 * @throws IOException * @author:[
[email protected]] */ public final static void filterImageByScale(String srcFileName) throws IOException{ MagickImage magic = null; try { ImageInfo imgInfo = new ImageInfo(srcFileName); magic = new MagickImage(imgInfo); int width = (int) magic.getDimension().getWidth(); int height = (int) magic.getDimension().getHeight(); MagickImage newImage = magic.scaleImage(width, height); newImage.profileImage("*", null); newImage.setImageAttribute("JPEG-Sampling-factors", null); newImage.setImageAttribute("comment", null); newImage.setFileName("d:/img/bbb.jpg"); newImage.writeImage(new ImageInfo()); magic.destroyImages(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args){ String srcFileName = "d:/img/aaa.png"; try{ filterImageByScale(srcFileName); }catch (IOException e){ e.printStackTrace(); } } }
这样,彻底解决了图片跨平台处理的问题,而且服务器也不依赖于init 5级别运行。