Manipulating imagine

Contents

Images are the staple of any graphical application, whether on the web or on the desk, images are everywhere. Having the ability to control and manipulate these images is a crucial skill that is absolutely necessary for any graphical artist, designer, or Game Engineer. This article will get you, the aspiring artist, professional designer, or amateur hobbyist, the foundations to be able to manipulate any image to your will. The end result will be twofold, you will have a nice Image Utility class for use in any of your Projects (I found it was most useful for 2d gaming), and you will have a small, but powerful application that can perform most common, and many not so common operations on. jpg's, .png's, .gif's, and bmp image files. This article assumes only that you have basic Java programming ability; of course a little prior Image experience would help. Know that I wont bog you down with trigonometric details. So, enough talking lets get coding!

Images in Java

Images in Java are defined by the java.awt.image.Image interface, which provides a basic model for controlling an image, but the real meat of the Java Image is the java.awt.image.BufferedImage . A BufferedImage is an accessible buffer of image data, essentially pixels, and their RGB colors. The BufferedImage provides a powerful way to manipulate the Image data. A BufferedImage object is made up of two parts a ColorModel object and a Raster object.

Figure 1. The &lt;CODE&gt;BufferedImage</CODE> Class
Figure 1. The BufferedImage Class

 

The ColorModel object provides methods that translate an image's pixel data into color components that can be understood, such as RGB for a computer.
The Raster represents a rectangular array of pixels. A Raster encapsulates a DataBuffer that stores the sample values and a SampleModel that describes how to locate a given sample value in a DataBuffer. Basically, the Raster holds two things, a DataBuffer , which contains the raw image data and a SampleModel , which describes how the data is organized in the buffer


Loading an Image

Probably the most common and most used image operation is loading an image. Loading an image is fairly straight forward, and there are many ways to do it, so I will show you one of the simplest and quickest ways, using the javax.imageio.ImageIO class.

Basically what we want to do is load all the bytes from some image file, into a BufferedImage so that we can display it. This can be accomplished through the use of ImageIO .

  1. public   static  BufferedImage loadImage(String ref) {  
  2.         BufferedImage bimg = null ;  
  3.         try  {  
  4.   
  5.             bimg = ImageIO.read(new  File(ref));  
  6.         } catch  (Exception e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.         return  bimg;  
  10.     }  
public static BufferedImage loadImage(String ref) {
		BufferedImage bimg = null;
		try {

			bimg = ImageIO.read(new File(ref));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return bimg;
	}

 

We would call the method like this ImageUtility.loadImage("C:/Folder/foo.bmp"); ImageIO reads in the bytes of an image to a BufferedImage . Once you have the BufferedImage , you can do many things with it. Of course loading an image is nearly completely useless unless you can display it on screen, so next ill explain how to render , or draw an image.

Drawing an Image on the Screen using Graphics2D

This is where the fun starts. To draw an Image, we need a surface to draw on; the Abstract Windowing Toolkit and Swing supply this. For a more detailed tutorial on those subjects, look up the java documentation at java.net. Every top-level container in Swing and AWT such as a JPanel, JFrame, or Canvas , has its own Graphics object. This Graphics object allows us to draw on its parent surface (a JPanel, JFrame, Canvas, or Image ) with such calls as Graphics.drawLine(int x1, int y1, int x2, int y2); or Graphics.drawImage(Image img, int x, int y, ImageObserver ob); The Graphics2D object, is a more powerful child of the Graphics object, it adds more methods which allow for drawing BufferedImages. Here is an example of how to load and draw a BufferedImage onto the screen.

  1. public   class  ImageApp {  
  2.   
  3.     public   void  loadAndDisplayImage(JFrame frame) {  
  4.         // Load the img   
  5.         BufferedImage loadImg = ImageUtil.loadImage("C:/Images/duke.gif" );  
  6.         frame.setBounds(00 , loadImg.getWidth(), loadImg.getHeight());  
  7.         // Set the panel visible and add it to the frame   
  8.         frame.setVisible(true );  
  9.         // Get the surfaces Graphics object   
  10.         Graphics2D g = (Graphics2D)frame.getRootPane().getGraphics();  
  11.         // Now draw the image   
  12.         g.drawImage(loadImg, null00 );  
  13.           
  14.     }  
  15.       
  16.     public   static   void  main(String[] args) {  
  17.         ImageApp ia = new  ImageApp();  
  18.         JFrame frame = new  JFrame( "Tutorials" );  
  19.         ia.loadAndDisplayImage(frame);  
  20.     }  
  21.       
  22. }  
public class ImageApp {

	public void loadAndDisplayImage(JFrame frame) {
		// Load the img
		BufferedImage loadImg = ImageUtil.loadImage("C:/Images/duke.gif");
		frame.setBounds(0, 0, loadImg.getWidth(), loadImg.getHeight());
		// Set the panel visible and add it to the frame
		frame.setVisible(true);
		// Get the surfaces Graphics object
		Graphics2D g = (Graphics2D)frame.getRootPane().getGraphics();
		// Now draw the image
		g.drawImage(loadImg, null, 0, 0);
		
	}
	
	public static void main(String[] args) {
		ImageApp ia = new ImageApp();
		JFrame frame = new JFrame("Tutorials");
		ia.loadAndDisplayImage(frame);
	}
	
}

In the first few lines we load the image using ImageUtil , then we change the JFrame that is passed in to our liking, and get the frames Graphics object. Note that we have to set the frame visible before we ask for the Graphics object. Then we use the graphics to draw the image at position (0, 0). Very simple, and very incomplete, you will notice that if you resize the screen, the image will disappear. To remedy this problem, we need to create a sub-class of JPanel that will facilitate showing images that will display no matter what the user circumstances. To do this we need to override the JPanel.paintComponent(Graphics g); method. I will call the sub-class JImagePanel , heres the class:

  1. public   class  JImagePanel  extends  JPanel{  
  2.     private  BufferedImage image;  
  3.     int  x, y;  
  4.     public  JImagePanel(BufferedImage image,  int  x,  int  y) {  
  5.         super ();  
  6.         this .image = image;  
  7.         this .x = x;  
  8.         this .y = y;  
  9.     }  
  10.     @Override   
  11.     protected   void  paintComponent(Graphics g) {  
  12.         super .paintComponent(g);  
  13.         g.drawImage(image, x, y, null );  
  14.     }  
  15. }  
public class JImagePanel extends JPanel{
	private BufferedImage image;
	int x, y;
	public JImagePanel(BufferedImage image, int x, int y) {
		super();
		this.image = image;
		this.x = x;
		this.y = y;
	}
	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		g.drawImage(image, x, y, null);
	}
}

This allows us to pass an image into the newly created JImagePanel , which will then handle all painting for the life cycle of the application. JImagePanel overrides paintComponent and makes it paint the Image at the supplied coordinates. Wrapping our images in this Panel will allow Swing and AWT to handle all rendering. Here is the previous example, except now using the JImagePanel :

  1. public   class  ImageApp {  
  2.   
  3.     public   void  loadAndDisplayImage(JFrame frame) {  
  4.         BufferedImage loadImg = ImageUtil.loadImage("C:/Images/duke.gif" );  
  5.         frame.setBounds(00 , loadImg.getWidth(), loadImg.getHeight());  
  6.         JImagePanel panel = new  JImagePanel(loadImg,  00 );  
  7.         frame.add(panel);  
  8.         frame.setVisible(true );  
  9.     }  
  10.     public   static   void  main(String[] args) {  
  11.         ImageApp ia = new  ImageApp();  
  12.         JFrame frame = new  JFrame( "Tutorials" );  
  13.         ia.loadAndDisplayImage(frame);  
  14.     }  
  15. }  
public class ImageApp {

	public void loadAndDisplayImage(JFrame frame) {
		BufferedImage loadImg = ImageUtil.loadImage("C:/Images/duke.gif");
		frame.setBounds(0, 0, loadImg.getWidth(), loadImg.getHeight());
		JImagePanel panel = new JImagePanel(loadImg, 0, 0);
		frame.add(panel);
		frame.setVisible(true);
	}
	public static void main(String[] args) {
		ImageApp ia = new ImageApp();
		JFrame frame = new JFrame("Tutorials");
		ia.loadAndDisplayImage(frame);
	}
}

This change makes a world of difference, as now the resizing, buffering, and other complicated stuff is handled internally, now all we have to worry about is manipulating images.


Creating and Using your own Image in Java

There are a few ways to create your own Image, all fairly simple. The most obvious way would be to simply create a new BufferedImage object. Like this:

  1. BufferedImage img =  new  BufferedImage( 100100 , BufferedImage.TYPE_3BYTE_BGR);  
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);

But to be able to draw into this image, you first need to create the Graphics :

  1. BufferedImage img =  new  BufferedImage( 100100 , BufferedImage.TYPE_3BYTE_BGR);  
  2. img.createGraphics();  
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_3BYTE_BGR);
img.createGraphics();

Now we have a surface to draw on, so, as an example, Ill draw a road on a yellow background;

  1. public   class  ImageMaker {  
  2.   
  3.     public   static  BufferedImage createImage() {  
  4.         BufferedImage img = new  BufferedImage( 100100 , BufferedImage.TYPE_INT_RGB);  
  5.         img.createGraphics();  
  6.         Graphics2D g = (Graphics2D)img.getGraphics();  
  7.         g.setColor(Color.YELLOW);  
  8.         g.fillRect(00100100 );  
  9.           
  10.         for ( int  i =  1 ; i <  49 ; i++) {  
  11.             g.setColor(new  Color( 5 *i,  5 *i,  4 + 1 * 2 +i* 3 ));  
  12.             g.fillRect(2 *i,  2 *i,  3 *i,  3 * 1 );  
  13.         }  
  14.         return  img;  
  15.     }  
  16.       
  17.     public   static   void  main(String[] args) {  
  18.         JFrame frame = new  JFrame( "Image Maker" );  
  19.         frame.addWindowListener(new  WindowAdapter() {  
  20.             public   void  windowClosing(WindowEvent event) {  
  21.                 System.exit(0 );  
  22.             }  
  23.         });  
  24.         frame.setBounds(00200200 );  
  25.         JImagePanel panel = new  JImagePanel(createImage(),  5045 );  
  26.         frame.add(panel);  
  27.         frame.setVisible(true );  
  28.     }  
  29.   
  30. }  
public class ImageMaker {

	public static BufferedImage createImage() {
		BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
		img.createGraphics();
		Graphics2D g = (Graphics2D)img.getGraphics();
		g.setColor(Color.YELLOW);
		g.fillRect(0, 0, 100, 100);
		
		for(int i = 1; i < 49; i++) {
			g.setColor(new Color(5*i, 5*i, 4+1*2+i*3));
			g.fillRect(2*i, 2*i, 3*i, 3*1);
		}
		return img;
	}
	
	public static void main(String[] args) {
		JFrame frame = new JFrame("Image Maker");
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent event) {
				System.exit(0);
			}
		});
		frame.setBounds(0, 0, 200, 200);
		JImagePanel panel = new JImagePanel(createImage(), 50, 45);
		frame.add(panel);
		frame.setVisible(true);
	}

}

This creates a BufferedImage , creates its graphics, then sets the background to yellow, lastly, using clever manipulation of a for loop, the program makes a rough drawing of a road. This method returns an image that is promptly displayed our custom JImagePanel . Note that I added WindowAdapter to the JFrame so that it will actually destroy the JVM when you stop the program. Below is a picture of the output:

Figure 2. The Road
Figure 2. The Lonesome Road

 

This is a small example of how you can make your own images. You can also use the Graphics2D methods to display a string in a particular font, draw ovals, predefined Shapes, and many other things.

Saving Images

Saving an image to a file is a fairly simple operation, basically, all that needs to happen is for bytes in memory to be written to a file. Below is the old way of saving a JPEG.

  1. public   static   void  saveOldWay(String ref, BufferedImage img) {  
  2.         BufferedOutputStream out;  
  3.         try  {  
  4.             out = new  BufferedOutputStream( new  FileOutputStream(ref));  
  5.             JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);  
  6.             JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img);  
  7.             int  quality =  5 ;  
  8.             quality = Math.max(0 , Math.min(quality,  100 ));  
  9.             param.setQuality((float ) quality /  100 .0f,  false );  
  10.             encoder.setJPEGEncodeParam(param);  
  11.             encoder.encode(img);  
  12.             out.close();  
  13.         } catch  (Exception e) {  
  14.             e.printStackTrace();  
  15.         }  
  16.     }  
public static void saveOldWay(String ref, BufferedImage img) {
		BufferedOutputStream out;
		try {
			out = new BufferedOutputStream(new FileOutputStream(ref));
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(img);
			int quality = 5;
			quality = Math.max(0, Math.min(quality, 100));
			param.setQuality((float) quality / 100.0f, false);
			encoder.setJPEGEncodeParam(param);
			encoder.encode(img);
			out.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

This saves your BufferedImage img to a JPEG file with a path specified by the String ref Basically, it encodes your image data into the JPEG format, sets the image quality, then saves it to a file. This archaic way of saving an image has many faults though, besides not being very straightforward. Thankfully Sun has given us ImageIO . ImageIO gives a centralized way to save and load images, for saving it has three forms of the write method, which writes your image data to a file in JPEG or PNG, format, the three forms of the write method are:

  • write(RenderedImage im, String formatName, File output)
  • write(RenderedImage im, String formatName, ImageOutputStream output)
  • write(RenderedImage im, String formatName, OutputStream output)

We can wrap our ImageUtil around one of these write methods, like this:

  1. /**  
  2.  * Saves a BufferedImage to the given file, pathname must not have any  
  3.  * periods "." in it except for the one before the format, i.e. C:/images/fooimage.png  
  4.  * @param img  
  5.  * @param saveFile  
  6.  */   
  7. public   static   void  saveImage(BufferedImage img, String ref) {  
  8.     try  {  
  9.         String format = (ref.endsWith(".png" )) ?  "png"  :  "jpg" ;  
  10.         ImageIO.write(img, format, new  File(ref));  
  11.     } catch  (IOException e) {  
  12.         e.printStackTrace();  
  13.     }  
  14. }  
	/**
	 * Saves a BufferedImage to the given file, pathname must not have any
	 * periods "." in it except for the one before the format, i.e. C:/images/fooimage.png
	 * @param img
	 * @param saveFile
	 */
	public static void saveImage(BufferedImage img, String ref) {
		try {
			String format = (ref.endsWith(".png")) ? "png" : "jpg";
			ImageIO.write(img, format, new File(ref));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}



Making an Image Translucent

Sometimes it is necessary to make an image, or part of an image somewhat translucent, or see through. If you have any experience with Photoshop, Gimp or other Imaging programs, this means modifying an Images alpha, or applying an Alpha layer on top of the Image. I will show you how to do the latter.

The Graphics2D object, which is a part of every image, and top level container (such as a JFrame ), lets you manipulate how it draws through multiple methods. Mainly, setComposite(Composite comp), setStroke(Stroke stroke) and setPaint(Paint paint) , all of these affect how the Graphics2D draws the image. For this part of the tutorial, we will mainly be concerned with setComposite(Composite comp) . Composites let us modify things like the transparency of an image, which is what we are interested in. Most of the time we only want the AlphaComposite on the Image, not on the entire Container. So we'll add another method to our ImageUtil class that will load an Image and set a certain amount of alpha on the image only, leaving the container alone. Here is the code:

  1. public   static  BufferedImage loadTranslucentImage(String url,  float  transperancy) {  
  2.         // Load the image   
  3.         BufferedImage loaded = loadImage(url);  
  4.         // Create the image using the    
  5.         BufferedImage aimg = new  BufferedImage(loaded.getWidth(), loaded.getHeight(), BufferedImage.TRANSLUCENT);  
  6.         // Get the images graphics   
  7.         Graphics2D g = aimg.createGraphics();  
  8.         // Set the Graphics composite to Alpha   
  9.         g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transperancy));  
  10.         // Draw the LOADED img into the prepared reciver image   
  11.         g.drawImage(loaded, null00 );  
  12.         // let go of all system resources in this Graphics   
  13.         g.dispose();  
  14.         // Return the image   
  15.         return  aimg;  
  16.     }  
public static BufferedImage loadTranslucentImage(String url, float transperancy) {
		// Load the image
		BufferedImage loaded = loadImage(url);
		// Create the image using the 
		BufferedImage aimg = new BufferedImage(loaded.getWidth(), loaded.getHeight(), BufferedImage.TRANSLUCENT);
		// Get the images graphics
		Graphics2D g = aimg.createGraphics();
		// Set the Graphics composite to Alpha
		g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transperancy));
		// Draw the LOADED img into the prepared reciver image
		g.drawImage(loaded, null, 0, 0);
		// let go of all system resources in this Graphics
		g.dispose();
		// Return the image
		return aimg;
	}

This block of code first loads your our image, and then creates another BufferedImage to draw into that allows translucency. Then, right after it gets the Graphics2D object of the image, it sets the composite to an AlphaComposite, with the alpha level set to whatever the user specifies. The amount of alpha can be any float value from 0.0 to 1.0. Next it draws the loaded image, into the translucency capable image, and disposes of unsued resources. Using our JImagePanel we draw the image and display it, giving us this:

Figure 3.Figure 3.Figure 4.
Fig. 3. Regular Duke
Fig. 4. Alpha Composited Duke


Making a Certain Color Transparent

A useful function of many imaging programs such as GIMP is its ability to make only one color of an Image transparent; this is called applying a mask. To do this, GIMP evaluates each pixel of an image, and tests if it is the user specified masking color, if it is it sets that pixel's RGB value to nothing, and it's alpha to 0.0. Here is the Java implementation:

  1. public   static  BufferedImage makeColorTransparent(String ref, Color color) {  
  2.         BufferedImage image = loadImage(ref);  
  3.         BufferedImage dimg = new  BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);  
public static BufferedImage makeColorTransparent(String ref, Color color) {
		BufferedImage image = loadImage(ref);
		BufferedImage dimg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);

This will load the image, and also sets up a destination image for drawing into. The next part is what does all the work:

  1.     Graphics2D g = dimg.createGraphics();  
  2.     g.setComposite(AlphaComposite.Src);  
  3.     g.drawImage(image, null00 );  
  4.     g.dispose();  
  5.     for ( int  i =  0 ; i < dimg.getHeight(); i++) {  
  6.         for ( int  j =  0 ; j < dimg.getWidth(); j++) {  
  7.             if (dimg.getRGB(j, i) == color.getRGB()) {  
  8.             dimg.setRGB(j, i, 0x8F1C1C );  
  9.             }  
  10.         }  
  11.     }  
  12.     return  dimg;  
  13. }  
		Graphics2D g = dimg.createGraphics();
		g.setComposite(AlphaComposite.Src);
		g.drawImage(image, null, 0, 0);
		g.dispose();
		for(int i = 0; i < dimg.getHeight(); i++) {
			for(int j = 0; j < dimg.getWidth(); j++) {
				if(dimg.getRGB(j, i) == color.getRGB()) {
				dimg.setRGB(j, i, 0x8F1C1C);
				}
			}
		}
		return dimg;
	}

This grabs the Graphics2D instance of the destination image, and sets it's composite to Alpha. It then draws the Image onto the screen, and disposes of the Graphics2D to clear up system resources. Next, it cycles through all the pixels in the image, using the for loops, and compares the color of the pixel to the mask color. If the pixels color matches the mask color, that pixels color is replaced by an alpha RGB value. Lastly, the destination image, with mask applied, is returned.

Here is what the output will be, when we wrap the manipulated image inside our JImagePanel :

Figure 5.Figure 5.Figure 6.
Fig. 5. Regular Duke
Fig. 6. Masked Duke

 

As you can see, all the white pixels on the image on the left were replaced with pixels of alpha value, shown in the window on the left.


Flipping an Image

Flipping an image means mirroring it on a certain axis. Flipping vertically could be compared to the reflection of yourself in a lake, flipping horizontally is kind of like looking into a mirror. To do a flip, we use drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver observer); of Graphics2D object. This method lets us draw part of an image into an image, or scale an image etc. The method takes an image, and a set of coordinates of the destination rectangle, it also takes a set of coordinates for the source rectangle of the image you supply. These source coordinates can be manipulated for varying results, as I will show in this next code segment.

For flipping an image horizontally, we give the source rectangle coordinates starting from the top right, and ending at the bottom left like this.

  1. drawImage(Image img,  int  dx1,  int  dy1,  int  dx2,  int  dy2,  int  sx1,  int  sy1,  int  sx2,  int  sy2, ImageObserver observer);  
  2.   
  3.     g.drawImage(<i>The Source Image</i>, 00 , <i>image width</i>, <i>image height</i>, <i>image width</i>,  0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值