天天看點

BMP檔案的讀取及儲存

Bmp

BMP是一種與硬體裝置無關的圖像檔案格式,使用非常廣。它采用位映射存儲格式,除了圖像深度可選以外,不采用其他任何壓縮,是以, BMP 檔案所占用的空間很大。 BMP 檔案的圖像深度可選 lbit 、 4bit 、 8bit 及 24bit 。 BMP 檔案存儲資料時,圖像的掃描方式是按從左到右、從下到上的順序。 由于 BMP 檔案格式是 Windows 環境中交換與圖有關的資料的一種标準,是以在 Windows 環境中運作的圖形圖像軟體都支援 BMP 圖像格式。

是在之前的畫圖闆基礎上添加的功能.

現在的界面很簡單:

BMP檔案的讀取及儲存
BMP檔案的讀取及儲存

之前的畫圖闆簡介:

1.建立JFrame視窗,添加三個JPanel,在左邊加上單選按鈕選擇形狀

2.在DrawListener中是想抽象的滑鼠擴充卡.

3.在正中間添加畫布,添加監聽器

4.畫直線,矩形,橢圓,畫筆功能.

5.重寫paint方法

6.建立Shape類,Line,Rect等類繼承Shape類,單獨實作繪制功能,用ArrayList<Shape> 存放繪制形狀的清單.順便加入撤銷功能.

7.儲存讀取檔案(實際是儲存List對象.)

24位BMP格式特點:包括BMP檔案頭 (14 位元組 ),位圖資訊頭(40 位元組 ),顔色表(24位沒有),資料. 四個部分

//BMP檔案頭

int bfType1= 0x42; // 位圖檔案的類型,必須為 ' B '' M '兩個字母 (0-1位元組 )   

int bfType2= 0x4d; 

int bfSize=14; // 位圖檔案的大小,以位元組為機關 (2-5 位元組 )   *********

int bfReserved1=0; // 位圖檔案保留字,必須為 0(6-7 位元組 )    

int  bfReserved2=0; // 位圖檔案保留字,必須為 0(8-9 位元組 )   

int  bfOffBits=54; // 位圖資料的起始位置,以相對于位圖 (10-13 位元組 ) 

//Bmp資訊頭

int Size=40; // 本結構所占用位元組數 (14-17 位元組 )  

int image_width=DrawListener.colors[0].length; // 位圖的寬度,以像素為機關 (18-21 位元組 )   

int image_height=DrawListener.colors.length; // 位圖的高度,以像素為機關 (22-25 位元組 )  

int Planes=1; // 目标裝置的級别,必須為 1(26-27 位元組 )   

int biBitCount=24;// 每個像素所需的位數,必須是 1(雙色),(28-29 位元組) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一  

int biCompression=0; // 位圖壓縮類型,必須是 0( 不壓縮 ),(30-33 位元組 ) 1(BI_RLE8 壓縮類型 ) 或// 2(BI_RLE4 壓縮類型 ) 之一  

int SizeImage=height*width; // 位圖的大小,以位元組為機關 (34-37 位元組 ) 

int biXPelsPerMeter=0; // 位圖水準分辨率,每米像素數 (38-41 位元組 )  0是預設值 

int biYPelsPerMeter=0; // 位圖垂直分辨率,每米像素數 (42-45 位元組 )  0是預設值

int biClrUsed=0;// 位圖實際使用的顔色表中的顔色數 (46-49 位元組 )        0說明全部用了

int biClrImportant=0;// 位圖顯示過程中重要的顔色數 (50-53 位元組 )    0說明全部重要

儲存bmp檔案就是先将這些資料儲存在檔案中,從第54位元組開始儲存位圖資料.每個像素點都按照bgr的順序(與red green blue相反)儲存在檔案中.每三個位元組合成一個像素點的顔色.

注意:由于獲得像素點顔色是用java提供的getRGB方法,傳回的是一個int整數,需要拆分成4個byte.從低到高位依次是bgra.也就是說,bytes[0]是藍色,bytes[1]是綠色,bytes[2]是紅色.

public byte[] int2byte(int data){
		byte[] bytes = {(byte)(((data)<<24)>>24),(byte)(((data)<<16)>>24),(byte)(((data)<<8)>>24),(byte)((data)>>24)};
		return bytes; 
	}
           

 将bytes[]合成int的方法(讀取檔案的時候用到)

public static int byte2int(byte b[]) {
		int b3 = b[3] & 0xff;
		int b2 = b[2] & 0xff;
		int b1 = b[1] & 0xff;
		int b0 = b[0] & 0xff;
		int i = b3 << 24 | b2 << 16 | b1 << 8 | b0;
		return i;
	}
           

完整的FileSave代碼:

public class FileSave {
	static int height,width;
	static int size;
	static JFileChooser jfc = new JFileChooser("D:\\");
	
	/**
	 * 儲存hb格式圖檔
	 * @param f 檔案
	 * @param b 形狀對象
	 */
	public static void saveHb(File f,ArrayList<Shape> b){
		try{
			FileOutputStream fos = new FileOutputStream(f);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
			size=b.size();
			oos.writeInt(size);//儲存size,避免重新打開程式後不知道要讀多少個對象,導緻無法讀取資料
			for(int i=0;i<size;i++){
				oos.writeObject(b.get(i));
			}
			
			oos.flush();
			oos.close();
			fos.close();
			
			
		}catch(IOException e){
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 儲存成bmp的方法
	 * @param f bmp檔案位址
	 */
	public void write24bmp(File f){
		

		//BMP檔案頭
		int bfType1= 0x42; // 位圖檔案的類型,必須為 ' B '' M '兩個字母 (0-1位元組 )   
		int bfType2= 0x4d; 
		int bfSize=14; // 位圖檔案的大小,以位元組為機關 (2-5 位元組 )   *********
		int bfReserved1=0; // 位圖檔案保留字,必須為 0(6-7 位元組 )    
		int  bfReserved2=0; // 位圖檔案保留字,必須為 0(8-9 位元組 )   
		int  bfOffBits=54; // 位圖資料的起始位置,以相對于位圖 (10-13 位元組 ) 

		//Bmp資訊頭
		int Size=40; // 本結構所占用位元組數 (14-17 位元組 )  
		int image_width=DrawListener.colors[0].length; // 位圖的寬度,以像素為機關 (18-21 位元組 )   
		int image_height=DrawListener.colors.length; // 位圖的高度,以像素為機關 (22-25 位元組 )  
		int Planes=1; // 目标裝置的級别,必須為 1(26-27 位元組 )   
		int biBitCount=24;// 每個像素所需的位數,必須是 1(雙色),(28-29 位元組) 4(16 色 ) , 8(256 色 ) 或 24(// 真彩色 ) 之一  
		int biCompression=0; // 位圖壓縮類型,必須是 0( 不壓縮 ),(30-33 位元組 ) 1(BI_RLE8 壓縮類型 ) 或// 2(BI_RLE4 壓縮類型 ) 之一  
		
		int SizeImage=height*width; // 位圖的大小,以位元組為機關 (34-37 位元組 ) 
		int biXPelsPerMeter=0; // 位圖水準分辨率,每米像素數 (38-41 位元組 )  0是預設值 
		int biYPelsPerMeter=0; // 位圖垂直分辨率,每米像素數 (42-45 位元組 )  0是預設值
		int biClrUsed=0;// 位圖實際使用的顔色表中的顔色數 (46-49 位元組 )        0說明全部用了
		int biClrImportant=0;// 位圖顯示過程中重要的顔色數 (50-53 位元組 )    0說明全部重要

		//個人了解,用byte定義這些屬性的話雖然不一定超出範圍,但是寫資料出去的時候不友善控制位元組數,例如size占4位元組,Planes占2位元組,如果用byte定義還要判斷填0
		//不如直接用int然後再轉為byte[4],再用write(byte[],開始位置,長度)這一個方法直接寫出去.
		
		//寫出去檔案頭資訊頭
		
		try {
			 FileOutputStream fos = new FileOutputStream(f);
			 BufferedOutputStream bos = new BufferedOutputStream(fos); 

			 bos.write(bfType1);
			 bos.write(bfType2);
			 bos.write(int2byte(bfSize),0,4);
			 bos.write(int2byte(bfReserved1),0,2);
			 bos.write(int2byte(bfReserved2),0,2);
			 bos.write(int2byte(bfOffBits),0,4);
			 bos.write(int2byte(Size),0,4);// 輸入資訊頭資料的總位元組數  
			 bos.write(int2byte(image_width),0,4);// 輸入位圖的寬  
			 bos.write(int2byte(image_height),0,4);// 輸入位圖的高
			 bos.write(int2byte(Planes),0,2);// 輸入位圖的目标裝置級别
			 bos.write(int2byte(biBitCount),0,2);// 輸入每個像素占據的位元組數
			 bos.write(int2byte(biCompression),0,4);// 輸入位圖的壓縮類型
			 bos.write(int2byte(SizeImage),0,4);// 輸入位圖的實際大小  
			 bos.write(int2byte(biXPelsPerMeter),0,4);// 輸入位圖的水準分辨率  
			 bos.write(int2byte(biYPelsPerMeter),0,4);// 輸入位圖的垂直分辨率  
			 bos.write(int2byte(biClrUsed),0,4);// 輸入位圖使用的總顔色數
			 bos.write(int2byte(biClrImportant),0,4);// 輸入位圖使用過程中重要的顔色數  
			 //24位沒有顔色表,是以接下來輸出顔色資訊.将我們的int數組變成byte[4]後将b[0] b[1] b[2] 依次作為blue green red輸出去
			 // 這裡周遊的時候注意,在計算機記憶體中位圖資料是從左到右,從下到上來儲存的,  
			 // 也就是說實際圖像的第一行的點在記憶體是最後一行 
			 for (int h = image_height - 1; h >= 0; h--) { 
				 for (int w = 0; w < image_width; w++) { 
					 // 這裡還需要注意的是,每個像素是有三個RGB顔色分量組成的,
					 // 而資料在windows作業系統下是小端存儲,對多位元組資料有用。
					 byte b[]=new byte[4];
					 b= int2byte(DrawListener.colors[h][w]);
					 byte red = b[2];// 得到紅色分量  
					 byte green = b[1];// 得到綠色分量
					 byte blue = b[0];// 得到藍色分量 
					 
					 bos.write(blue);
					 bos.write(green);  
					 bos.write(red); 
				 } 
			 } 
			 //關閉資料的傳輸  
			 bos.flush(); 
			 bos.close(); 
			 fos.close(); 
			 System.out.println("寫入成功!!!");
			 
			 
		} catch (IOException e) {
			e.printStackTrace();
		} 
		 
		
		
		
	}
	
	public byte[] int2byte(int data){
		byte[] bytes = {(byte)(((data)<<24)>>24),(byte)(((data)<<16)>>24),(byte)(((data)<<8)>>24),(byte)((data)>>24)};
		//{BGRA}
		return bytes; 
	}
	
	public static int byte2int(byte b[]) {
		int b3 = b[3] & 0xff;
		int b2 = b[2] & 0xff;
		int b1 = b[1] & 0xff;
		int b0 = b[0] & 0xff;
		int i = b3 << 24 | b2 << 16 | b1 << 8 | b0;
		return i;
	}
  

	
	/**
	 * 讀取bmp的方法
	 * @param f
	 */
	public static void read24bmp(File f,Graphics g) {

		try {
			FileInputStream fis = new FileInputStream(f);
			BufferedInputStream bis = new BufferedInputStream(fis);
			try {
				int [][]data = null;
				if (bis.read() == 66 && bis.read() == 77) {
					bis.skip(16);// 先去除才開始18個,再取出4個byte拼成一個int型的寬度
					byte wi[] = new byte[4];
					bis.read(wi);
					width = byte2int(wi);
					byte he[] = new byte[4];
					bis.read(he);
					height = byte2int(he);
					data = new int[height][width];
					bis.skip(28);
					int skipNum=4-width*3%4;
					for (int h = height - 1; h >= 0; h--) {
						for (int w = 0; w < width; w++) {
							
							//
							int b = bis.read();
							int green = bis.read();
							int r = bis.read();
							Color c = new Color(r,green,b);
							data[h][w] = c.getRGB();
						}
						if (skipNum != 4) {
							bis.skip(skipNum);
						}


						
					}
					bis.close();
					fis.close();
					DrawUI.center.setPreferredSize(new Dimension(width,height));  
		            javax.swing.SwingUtilities.updateComponentTreeUI(DrawUI.center);
					
				}

				if (data != null) {
					for (int i = 0; i < data.length; i++) {
						for (int j = 0; j < data[i].length; j++) {
							Color c = new Color(data[i][j]);
							Line line = new Line(j, i, j, i, c);
							line.draw(g);
							if(data[i][j]!=-1){
								DrawListener.list.add(line);
							}
							
						}
					}

				}
				
			} catch (IOException e) {
				e.printStackTrace();
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

	}
	
	/**
	 * 讀取hb格式圖檔
	 * @param f 檔案
	 * @param b 形狀對象
	 */
	public static ArrayList readHb(File f){
		ArrayList<Shape> b=new ArrayList<Shape>();
		try{
			FileInputStream fis = new FileInputStream(f);
			ObjectInputStream ois = new ObjectInputStream(fis);
			size = ois.readInt();
			for(int i=0;i<size;i++){
					try {
						b.add((Shape) ois.readObject());
					} catch (ClassNotFoundException e) {
						e.printStackTrace();
					}
				
		
			}	
			ois.close();
			fis.close();
			return b;
		}catch(EOFException e){
			System.out.println("讀取上次自動儲存的檔案發生錯誤.");
		}
		catch(IOException e){
			e.printStackTrace();
		}
		return null;
	}
	
	public static void read(Graphics g){
		int state = jfc.showOpenDialog(null);
		if (state == 0) {// 點選了打開按鈕
			File f = new File(jfc.getSelectedFile().getAbsolutePath());
			try {
				FileInputStream fis = new FileInputStream(f);
				BufferedInputStream bis = new BufferedInputStream(fis);
				if (bis.read() == 66 && bis.read() == 77) {
					bis.close();
					fis.close();
					read24bmp(f,g);
				}else{
					bis.close();
					fis.close();
					
					DrawListener.list =readHb(f);
					
				}
			}catch(IOException e){
				e.printStackTrace();
			}
			
		}
		
	}
	
}
           

其餘類代碼參考: