使用JAVA对图片进行效果变换
原文链接: http://www.ismyway.com/articles/simpleimageeffect/index.html
一提到JAVA,谈论最多的就是JSP/SERVLET/J2EE之类的,但对于用JAVA对图片进行效果变换,到论坛里看了看,关于这方面的话题并不多,网上关于图像效果处理的文章也并不丰富,于是在自己摸索了几天,并且参考了AnfyJava(专业级的JAVA效果生成器)后,我用轻量级控件写了一个(AnfyJava继承的是Applet,Applet是java.awt包中的,属于重量级控件,SUN现在推荐使用swing来编写图形程序,因此,我用的是JApplet)。
其实,用JAVA做图像效果和其它语言在本质上并没有什么区别,只不过在实现起来有所不同罢了,下面我就把我在项目中处理的经验与大家分享一下吧。
图像的变换,实际上就是把两幅图片的内容进行某些运算,生成新的图像,然后显示出来,最终实现从一幅图片到另一幅图片的过度效果。变换的具体过程如下:
在上面的过程中,图片A和B的尺寸最好保持一致,如果不一致的话,可能要做一些额外的处理,在此,我选用的图片A和B的尺寸是一致的。
首先,我们将其当作一个Applet来写,由于Applet的局限性,不可以直接使用File类来读取图像文件,因此,我们只能通过如下方法来获取图像文件。
URLClassLoader urlLoader = (URLClassLoader)this.getClass().getClassLoader(); URL url = urlLoader.findResource("imagea.gif"); Image image = Toolkit.getDefaultToolkit().getImage(url);
当我们获得了图像后,可以通过java.awt.image.PixelGrabber包中的PixelGrabber方法来将图像中的像素信息完全读取出来,其用法如下:
PixelGrabber(Image img, int x, int y, int w, int h, int[] pix, int off, int scansize)
其中img是要读取的图像,x/y是要读取图像中的左上角坐标,w/h分别是从x/y开始起的距离,其实x,y,w,h就是一个矩形,pix是保存像素的数组,off是读取图像时开始的位置,scansize是指扫描的宽度,一般来说和w相等。
int width = image.getWidth(); int height = image.getHeight(); int size = width * height; int[] pixels = new int[size]; pixelgrabber = new PixelGrabber(image, 0, 0, width, height, pixels, 0, width); try { pixelgrabber.grabPixels(); //读取像素入数组 } catch (InterruptedException _ex) {}
由于像素信息是由alpha,red,green,blue组成的,其格式为
int alpha = (pixel >> 24) & 0xff; int red = (pixel >> 16) & 0xff; int green = (pixel >> 8) & 0xff; int blue = (pixel) & 0xff;
假如要实现显示图片A后,图片B由上至下展开,则可以每次将图片A中的一行像素替换为B中的相应行,然后生成新的像素信息:
old = pixelA; //保存图片A的像素信息 oldR = redA; //保存图片A的R信息 oldG = greenA; //保存图片A的G信息 oldB = blueA; //保存图片A的B信息 for (int i = 0; i < width; i++) {//line为行数 oldR[line * width + i] = redA [line * width + i]; oldG[line * width + i] = greenA [line * width + i]; oldB[line * width + i] = blueA [line * width + i]; old[line * width + i] = oldR[line * width + i] << 16 + oldG[line * width + i] << 8 + oldB[line * width + i]; }
当生成新的像素信息后,可以通过java.awt.image.MemoryImageSource包中的MemoryImageSource(int w, int h, ColorModel cm, int[] pix, int off, int scan)方法将像素数组对应到图像,并且可以用newPixels()方法来生成新的图像(具体用法可以参考JAVA API DOC)。
memoryimagesource = new MemoryImageSource(imageWidth, imageHeight, new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff), blocks, 0, imageWidth); //检查java版本 String javaVersion; try { javaVersion = System.getProperty("java.version"); } catch (SecurityException _ex) { javaVersion = "unk"; } if (!javaVersion.startsWith("1.0")) { //jdk1.1以上的版本才支持此方法 try { memoryimagesource.setAnimated(true); memoryimagesource.setFullBufferUpdates(true); imageBuf = createImage(memoryimagesource); memoryimagesource.newPixels();//生成新的图像 } catch (NoSuchMethodError _ex) { System.out.println("unknow java version!"); } }
到此,新的图像已经产生,只需要输出到屏幕即可。
在此,需要注意以下几个问题:
1、 由于Applet读取的图像文件可以比较大,对于速度较慢的网络,可能会造成图像未读取完全就开始进行变换,因此,建议使用MediaTracker方法来保证图像能被顺利载入。
2、 在显示的时候,为了避免闪烁,可以采用双缓冲的方法。
3、 由于在某此高速计算机上,生成新的像素可以非常快,为了避免速度过快而造成效果并不明显,可以加以适当的延时处理。
4、 为了保证效果的平滑,我们特地开辟了非常大的数组来保存像素/RGB信息,这是以空间换时间的做法。
完整的源程序附下(在jdk1.4/2k Server/RedHat9下运行通过,所用机器为P4 2.4G/512M)
/*程序只给出了一个从上向下展开的例子,并且申明的变量redA等并没有使用,如果考虑到要做其它更复杂的效果变换,可以查阅相关资料,找到各种变换效果的公式即可(此时redA等就会用上了)*/
package pic; import java.awt.*; import java.io.*; import java.net.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; public class effect extends JApplet implements Runnable { //定义变量 Toolkit toolkit; int totalBlocks = 0; //图像被分解成的块数,默认为 宽X高 int[] blocks; //保存块数的信息 Image[] bufferImage = new Image[2]; //屏幕上的图形缓冲 VolatileImage offScreenImage; Image imageBuf; //保存图片缓冲区内容 Graphics2D offScreenGraphics; Thread thread; MediaTracker mediaTracker; boolean[] isImageReady; //图片是否已经装载 MemoryImageSource memoryimagesource; int imageWidth, imageHeight; //图像的宽及高 int[] pixelA, pixelB; int[] redA, greenA, blueA, redB, greenB, blueB; public effect() throws HeadlessException { bufferImage[0] = getImage("a.jpg"); bufferImage[1] = getImage("b.jpg"); if ( (bufferImage[0].getWidth(this) != bufferImage[1].getWidth(this)) || (bufferImage[0].getHeight(this) != bufferImage[1].getHeight(this))) { System.out.println("图像尺寸不一致!"); return; } toolkit = getToolkit(); imageWidth = bufferImage[0].getWidth(this); imageHeight = bufferImage[0].getHeight(this); totalBlocks = imageWidth * imageHeight; //计算分解的块数 blocks = new int[totalBlocks]; pixelA = new int[totalBlocks]; pixelB = new int[totalBlocks]; redA = new int[totalBlocks]; greenA = new int[totalBlocks]; blueA = new int[totalBlocks]; redB = new int[totalBlocks]; greenB = new int[totalBlocks]; blueB = new int[totalBlocks]; GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gd = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gd.getDefaultConfiguration(); offScreenImage = gc.createCompatibleVolatileImage(imageWidth, imageHeight); //创建图像缓冲 offScreenGraphics = offScreenImage.createGraphics(); //取得缓冲的graphics对象 } public void init() { getImagePixels(bufferImage[0], pixelA); getImagePixels(bufferImage[1], pixelB); for (int i = 0; i < totalBlocks; i++) { blocks[i] = pixelA[i]; //保存图像A的像素信息 redA[i] = pixelA[i] & 0xff0000; //保存图像B的red值 greenA[i] = pixelA[i] & 0x00ff00; //保存图像B的green值 blueA[i] = pixelA[i] & 0x0000ff; //保存图像B的blue值 redB[i] = pixelB[i] & 0xff0000; //保存图像B的red值 greenB[i] = pixelB[i] & 0x00ff00; //保存图像B的green值 blueB[i] = pixelB[i] & 0x0000ff; //保存图像B的blue值 } prepareImagePixels(); //将像素信息还原为图像 } public void run() { //检查java版本 String javaVersion; try { javaVersion = System.getProperty("java.version"); } catch (SecurityException _ex) { javaVersion = "unk"; } if (javaVersion.startsWith("1.0")) { System.out.println("require java 1.1 or later version!"); return; } try { //暂停3秒钟后等待效果开始 thread.sleep(3000l); } catch (InterruptedException ex1) { } int line = 0; Thread currentThread = Thread.currentThread(); while (line < imageHeight && thread == currentThread) { for (int i = 0; i < imageWidth; i++) { int offset = line * imageWidth + i; blocks[offset] = pixelB[offset]; //与下一被注释的语句作用相同 //blocks[offset] = redB[offset] | greenB[offset] | blueB[offset]; } memoryimagesource.newPixels(); //生成新的图像 line++; repaint(); //适当延时 try { thread.sleep(20l); } catch (InterruptedException ex) { } } } public void paint(Graphics g) { if (offScreenGraphics != null) { //保证在destory()时不引发异常 offScreenGraphics.drawImage(imageBuf, 0, 0, this); g.drawImage(offScreenImage, 0, 0, this); } } public void start() { if (thread == null) { thread = new Thread(this); thread.start(); } } public void stop() { thread = null; } public final void update(Graphics g) { paint(g); } public void destroy() { if (offScreenImage != null) { offScreenImage.flush(); } offScreenImage = null; if (offScreenGraphics != null) { offScreenGraphics.dispose(); } offScreenGraphics = null; System.gc(); } /** * 通过给定的文件名获得图像 * @param filename 给定图像的文件名 * @return 图像 */ Image getImage(String filename) { URLClassLoader urlLoader = (URLClassLoader)this.getClass().getClassLoader(); URL url = null; Image image = null; url = urlLoader.findResource(filename); image = Toolkit.getDefaultToolkit().getImage(url); MediaTracker mediatracker = new MediaTracker(this); try { mediatracker.addImage(image, 0); mediatracker.waitForID(0); } catch (InterruptedException _ex) { image = null; } if (mediatracker.isErrorID(0)) { image = null; } return image; } /** * 取得给定图像的像素数组 * @param image 指定的图像 * @param pixels 保存像素信息的数组 * @return 成功返回true */ private boolean getImagePixels(Image image, int pixels[]) { PixelGrabber pixelgrabber = new PixelGrabber(image, 0, 0, imageWidth, imageHeight, pixels, 0, imageWidth); try { pixelgrabber.grabPixels(); } catch (InterruptedException _ex) { return false; } return true; } /** * 将像素数组还原为图像 */ void prepareImagePixels() { memoryimagesource = new MemoryImageSource(imageWidth, imageHeight, new DirectColorModel(24, 0xff0000, 0x00ff00, 0x0000ff), blocks, 0, imageWidth); try { memoryimagesource.setAnimated(true); memoryimagesource.setFullBufferUpdates(true); imageBuf = createImage(memoryimagesource); memoryimagesource.newPixels(); //生成新的图像 } catch (NoSuchMethodError _ex) { } } /** * 取得图像的宽度 * @return 宽度 */ public int getWidth() { return imageWidth; } /** * 取得图像的高度 * @return 高度 */ public int getHeight() { return imageHeight; } public static void main(String args[]) { JFrame frame = new JFrame("Demo"); effect e = new effect(); e.init(); e.start(); frame.getContentPane().setLayout(new BorderLayout()); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setResizable(false); frame.getContentPane().add(e); frame.setSize(new Dimension(e.getWidth() + 6, e.getHeight() + 20)); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Dimension frameSize = frame.getSize(); frame.setLocation( (screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2); frame.show(); } }
上一篇: 外媒发现搭载锐龙7 3700U的联想笔记本比惠普快58%
下一篇: 产妇海带汤的做法
推荐阅读
-
使用base64对图片的二进制进行编码并用ajax进行显示
-
java使用DOM对XML文档进行增删改查操作实例代码
-
使用python opencv对目录下图片进行去重的方法
-
pscc2017怎么使用智能滤镜对水珠进行模糊处理以产生立体效果?
-
使用Scrapy自带的ImagesPipeline下载图片,并对其进行分类。
-
在Python中使用PIL模块对图片进行高斯模糊处理的教程
-
Java使用poi对Excel进行读和写处理
-
java 使用面向对象方式实现录入学生信息,取出成绩最大值、最小值、平均值、对其进行排序
-
荐 java 怎么使用 设计模式对业务进行解耦(一)
-
使用Java8 Stream API对Map按键或值进行排序